diff --git a/.gitguardian b/.gitguardian
new file mode 100644
index 00000000..809ac388
--- /dev/null
+++ b/.gitguardian
@@ -0,0 +1,4 @@
+secret:
+ # Exclude files and paths by globbing
+ ignored_paths:
+ - 'provision-contest/ansible/group_vars/**/secret.yml.example'
diff --git a/.github/linting.sh b/.github/linting.sh
index 65ae7b8a..25c9b386 100755
--- a/.github/linting.sh
+++ b/.github/linting.sh
@@ -5,10 +5,10 @@ set -euxo pipefail
# shellcheck disable=SC2044
for book in $(find ./ -maxdepth 1 -name "*.yml"); do
if [ "$book" != "./handlers.yml" ]; then
- ansible-lint "$book" -x braces,line-length
+ ansible-lint "$book" -x 'braces,yaml[line-length]'
fi
done
# shellcheck disable=SC2044
for dir in $(find ./roles -maxdepth 1 -type d); do
- ansible-lint "$dir" -x braces,line-length
+ ansible-lint "$dir" -x 'braces,yaml[line-length]'
done
diff --git a/.github/workflows/ansible-linting.yml b/.github/workflows/ansible-linting.yml
deleted file mode 100644
index 2bc0eeeb..00000000
--- a/.github/workflows/ansible-linting.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Test contest deployment (ansible scripts)
-
-on: [push,pull_request]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repo
- uses: actions/checkout@v2
- - name: Install ansible lint tools
- run: sudo apt update; sudo pip install ansible-lint
- - name: Lint the different scripts
- run: |
- set -eux
- ansible-lint --version
- ansible-lint .
- working-directory: provision-contest/ansible
- - name: Lint the different scripts (Via the script)
- run: ../../.github/linting.sh
- working-directory: provision-contest/ansible
-
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index 91187d0a..1594e8eb 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -7,7 +7,7 @@ jobs:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
diff --git a/contest-api/check-api-consistency.php b/contest-api/check-api-consistency.php
index eebfb44c..9229c753 100755
--- a/contest-api/check-api-consistency.php
+++ b/contest-api/check-api-consistency.php
@@ -36,6 +36,10 @@ function warning($msg)
foreach ($feed_json as $row) {
$endpoint = $row['type'];
+ if ($endpoint === 'contest') {
+ // New feed format uses singular for contest
+ $endpoint = 'contests';
+ }
$id = isset($row['data']['id']) ? $row['data']['id'] : '_single_';
$feed_data[$endpoint][$id][] = $row;
}
@@ -62,30 +66,39 @@ function array_diff_keys($a, $b)
error("'state' cannot have ID '$id' set.");
}
for ($i=0; $i $rows) {
- if ($rows[0]['op']!=='create') {
+ if (isset($rows[0]['op']) && $rows[0]['op']!=='create') {
error("'$endpoint/$id' not created first.");
}
for ($i=1; $i $elements) {
foreach ($elements as $id => $rows) {
$last = end($rows);
- if ($last['op']!=='delete') {
+ if ((isset($last['op']) && $last['op']!=='delete') || (!isset($last['op']) && isset($last['data'])) ) {
if (!isset($endpoint_data[$endpoint][$id])) {
error("'$endpoint".($id==='_single_' ? '' : "/$id")."' not found in REST endpoint.");
} elseif ($last['data']!==$endpoint_data[$endpoint][$id]) {
diff --git a/contest-api/check-api.sh b/contest-api/check-api.sh
index 7e611d74..791c1bb7 100755
--- a/contest-api/check-api.sh
+++ b/contest-api/check-api.sh
@@ -140,17 +140,11 @@ while getopts 'a:Cc:dehj:npt:q' OPT ; do
t) FEED_TIMEOUT="$OPTARG" ;;
q) QUIET=1 ;;
:)
- error "option '$OPTARG' requires an argument."
- exit 1
- ;;
+ error "option '$OPTARG' requires an argument." ;;
?)
- error "unknown option '$OPTARG'."
- exit 1
- ;;
+ error "unknown option '$OPTARG'." ;;
*)
- error "unknown error reading option '$OPT', value '$OPTARG'."
- exit 1
- ;;
+ error "unknown error reading option '$OPT', value '$OPTARG'." ;;
esac
done
shift $((OPTIND-1))
@@ -161,7 +155,6 @@ API_URL="$1"
if [ -z "$API_URL" ]; then
error "API URL argument expected."
- exit 1
fi
TMP=$(mktemp -d)
@@ -327,6 +320,7 @@ $ENDPOINT"
fi
if [ -n "$CHECK_CONSISTENCY" ]; then
+ ENDPOINTS_CHECK_CONSISTENT="${ENDPOINTS_CHECK_CONSISTENT/accounts/}"
# shellcheck disable=SC2086
eval ${EXTRAPROP:-STRICT=1} "$MYDIR"/check-api-consistency.php "$TMP/$CONTEST" $ENDPOINTS_CHECK_CONSISTENT
EXIT=$?
diff --git a/contest-api/json-schema/common.json b/contest-api/json-schema/common.json
index f7d0811e..e0ed6fd6 100644
--- a/contest-api/json-schema/common.json
+++ b/contest-api/json-schema/common.json
@@ -27,6 +27,30 @@
]
},
+ "endpointssingularcontest": {
+ "enum": [
+ "contest",
+ "judgement-types",
+ "languages",
+ "problems",
+ "groups",
+ "organizations",
+ "persons",
+ "team-members",
+ "accounts",
+ "teams",
+ "state",
+ "submissions",
+ "judgements",
+ "runs",
+ "clarifications",
+ "awards",
+ "commentary",
+ "scoreboard",
+ "event-feed"
+ ]
+ },
+
"abstime": {
"type": "string",
"pattern": "^[12][0-9]{3}-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](\\.[0-9]{3})?([+-][0-1][0-9](:[0-5][0-9])?|Z)$"
@@ -120,10 +144,12 @@
"fileref": {
"type": "object",
"properties": {
- "href": { "type": "string" },
- "mime": { "type": "string" },
- "width": { "type": "integer", "minimum": 1 },
- "height": { "type": "integer", "minimum": 1 }
+ "href": { "type": "string" },
+ "mime": { "type": "string" },
+ "hash": { "type": "string" },
+ "filename": { "type": "string" },
+ "width": { "type": "integer", "minimum": 1 },
+ "height": { "type": "integer", "minimum": 1 }
},
"required": ["href", "mime"],
"$ref": "#/strictproperties"
diff --git a/contest-api/json-schema/contest.json b/contest-api/json-schema/contest.json
index e2ff4230..33135643 100644
--- a/contest-api/json-schema/contest.json
+++ b/contest-api/json-schema/contest.json
@@ -12,6 +12,7 @@
"countdown_pause_time": { "$ref": "common.json#/posreltimeornull" },
"duration": { "$ref": "common.json#/posreltime" },
"scoreboard_freeze_duration": { "$ref": "common.json#/posreltimeornull" },
+ "scoreboard_thaw_time": { "$ref": "common.json#/abstimeornull" },
"penalty_time": {
"type": "integer",
"minimum": 0
diff --git a/contest-api/json-schema/event-feed-array.json b/contest-api/json-schema/event-feed-array.json
index 69d102c8..0f4d7311 100644
--- a/contest-api/json-schema/event-feed-array.json
+++ b/contest-api/json-schema/event-feed-array.json
@@ -4,9 +4,12 @@
"description": "JSON array of responses of this NDJSON API call",
"type": "array",
- "uniqueItems": true,
"$ref": "common.json#/nonemptyarray",
"items": {
- "$ref": "event-feed.json#"
+ "$comment": "Use anyOf since the event type fields overlap between the current and legacy format.",
+ "anyOf": [
+ { "$ref": "event-feed.json#" },
+ { "$ref": "legacy-event-feed.json#" }
+ ]
}
}
diff --git a/contest-api/json-schema/event-feed.json b/contest-api/json-schema/event-feed.json
index 656f5e05..cce6d633 100644
--- a/contest-api/json-schema/event-feed.json
+++ b/contest-api/json-schema/event-feed.json
@@ -5,13 +5,23 @@
"type": "object",
"properties": {
- "id": { "$ref": "common.json#/identifier" },
- "type": { "$ref": "common.json#/endpoints" }
+ "id": {
+ "oneOf": [
+ { "$ref": "common.json#/identifier" },
+ { "type": "null" }
+ ]
+ },
+ "token": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "null" }
+ ]
+ },
+ "type": { "$ref": "common.json#/endpointssingularcontest" }
},
"oneOf": [
{
"properties": {
- "op": { "enum": [ "create", "update" ] },
"data": {
"$comment": "Use anyOf since some types match others without strict attribute checking.",
"anyOf": [
@@ -38,18 +48,12 @@
},
{
"properties": {
- "op": { "enum": [ "delete" ] },
"data": {
- "type": "object",
- "properties": {
- "id": { "$ref": "common.json#/identifier" }
- },
- "required": ["id"],
- "$ref": "common.json#/strictproperties"
+ "type": "null"
}
}
}
],
- "required": ["id", "type", "op", "data"],
+ "required": ["id", "type", "data"],
"$ref": "common.json#/strictproperties"
}
diff --git a/contest-api/json-schema/legacy-event-feed.json b/contest-api/json-schema/legacy-event-feed.json
new file mode 100644
index 00000000..656f5e05
--- /dev/null
+++ b/contest-api/json-schema/legacy-event-feed.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CLICS Contest API: event-feed",
+ "description": "Single line response of this NDJSON API call",
+
+ "type": "object",
+ "properties": {
+ "id": { "$ref": "common.json#/identifier" },
+ "type": { "$ref": "common.json#/endpoints" }
+ },
+ "oneOf": [
+ {
+ "properties": {
+ "op": { "enum": [ "create", "update" ] },
+ "data": {
+ "$comment": "Use anyOf since some types match others without strict attribute checking.",
+ "anyOf": [
+ { "$ref": "contest.json#" },
+ { "$ref": "judgement-type.json#" },
+ { "$ref": "language.json#" },
+ { "$ref": "problem.json#" },
+ { "$ref": "group.json#" },
+ { "$ref": "organization.json#" },
+ { "$ref": "team.json#" },
+ { "$ref": "person.json#" },
+ { "$ref": "team-member.json#" },
+ { "$ref": "account.json#" },
+ { "$ref": "state.json#" },
+ { "$ref": "submission.json#" },
+ { "$ref": "judgement.json#" },
+ { "$ref": "run.json#" },
+ { "$ref": "clarification.json#" },
+ { "$ref": "award.json#" },
+ { "$ref": "commentary.json#" }
+ ]
+ }
+ }
+ },
+ {
+ "properties": {
+ "op": { "enum": [ "delete" ] },
+ "data": {
+ "type": "object",
+ "properties": {
+ "id": { "$ref": "common.json#/identifier" }
+ },
+ "required": ["id"],
+ "$ref": "common.json#/strictproperties"
+ }
+ }
+ }
+ ],
+ "required": ["id", "type", "op", "data"],
+ "$ref": "common.json#/strictproperties"
+}
diff --git a/contest-api/json-schema/problem.json b/contest-api/json-schema/problem.json
index e570d9a4..6f250687 100644
--- a/contest-api/json-schema/problem.json
+++ b/contest-api/json-schema/problem.json
@@ -31,7 +31,9 @@
"test_data_count": {
"type": "integer",
"minimum": 0
- }
+ },
+ "package": { "$ref": "common.json#/filerefsornull" },
+ "statement": { "$ref": "common.json#/filerefsornull" }
},
"required": ["id", "label", "name", "ordinal", "test_data_count"],
"$ref": "common.json#/strictproperties"
diff --git a/contest-api/json-schema/scoreboard.json b/contest-api/json-schema/scoreboard.json
index aeda4e46..d2cff4bc 100644
--- a/contest-api/json-schema/scoreboard.json
+++ b/contest-api/json-schema/scoreboard.json
@@ -72,5 +72,5 @@
}
}
},
- "required": ["event_id", "time", "contest_time", "state", "rows"]
+ "required": ["time", "contest_time", "state", "rows"]
}
diff --git a/domlogo/domlogo.py b/domlogo/domlogo.py
index f8bd7285..4272537d 100755
--- a/domlogo/domlogo.py
+++ b/domlogo/domlogo.py
@@ -1,13 +1,80 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
+import subprocess
-import PySimpleGUI as sg
+import FreeSimpleGUI as sg
import glob
import os
import requests
import re
import time
+import platform
+import shlex
+import yaml
+from PIL import Image, ImageDraw, ImageFont
+
+
+def generate_placeholder(text: str, path: str, background_color, width: int, height: int):
+ image = Image.new("RGB", (width, height), background_color)
+ draw = ImageDraw.Draw(image)
+ font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 72)
+ text_bbox = draw.textbbox((0, 0), text, font=font)
+ text_width = text_bbox[2] - text_bbox[0]
+ text_height = text_bbox[3] - text_bbox[1]
+ text_x = (width - text_width) / 2
+ text_y = (height - text_height) / 2
+ draw.text((text_x, text_y), text, fill=(255,255,255), font=font)
+
+ # Add some hint how to replace the image.
+ small_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16)
+ placeholder_text = f'(Replace this placeholder image at {path})'
+ text_bbox = draw.textbbox((0, 0), placeholder_text, font=small_font)
+ text_width = text_bbox[2] - text_bbox[0]
+ text_x = (width - text_width) / 2
+ text_y = text_y + 1.4*text_height
+ draw.text((text_x, text_y), placeholder_text, fill=(195,196,199), font=small_font)
+ image.save(path)
+
+
+def download_image(image_type: str, entity_id: str, file: dict):
+ href = file['href']
+ filename = file['filename']
+ photo_head = requests.head(f'{api_url}/{href}', auth=(user, passwd))
+ etag = photo_head.headers['ETag']
+ etag_file = f'domlogo-files/{image_type}s/{entity_id}.etag.txt'
+ temp_file = f'domlogo-files/{image_type}s/temp-{entity_id}-{filename}'
+ existing_etag = None
+ if os.path.isfile(etag_file):
+ with open(etag_file) as f:
+ existing_etag = f.readline().strip()
+
+ if existing_etag != etag:
+ print(f'Downloading and converting {image_type} for entity with ID {entity_id}...')
+ os.makedirs(os.path.dirname(temp_file), exist_ok=True)
+ with open(temp_file, 'wb') as f:
+ f.write(requests.get(f'{api_url}/{href}', auth=(user, passwd)).content)
+
+ return True, temp_file, etag_file, etag
+
+ return False, None, None, None
+
font = ('Roboto', 14)
+mono_font = ('Liberation Mono', 32)
+host = platform.node()
+host_bg_color = 'black'
+
+idle_image = 'domlogo-files/photos/idle.png'
+if not os.path.isfile(idle_image):
+ os.makedirs(os.path.dirname(idle_image), exist_ok=True)
+ generate_placeholder('judgehost idle', idle_image, (10,75,120), 1024, 768)
+
+config_file = 'domlogo-files/config.yaml'
+if os.path.isfile(config_file) and os.access(config_file, os.R_OK):
+ with open(config_file, 'r') as f:
+ config = yaml.safe_load(f)
+ if 'host-bg-color' in config:
+ host_bg_color = config['host-bg-color']
+
team_image = sg.Image(filename='domlogo-files/photos/idle.png')
metadata_text = sg.Text('No submissions in queue.', font=font)
results_text = sg.Text('', font=font)
@@ -21,9 +88,10 @@
[sg.Image(filename=c[0]), sg.Text(c[1], font=font), sg.Canvas(size=(10,50))] for c in cache
]
layout = [
+ [sg.Push(), sg.Text(f'{host}', font=mono_font, background_color=host_bg_color), sg.Push()],
[sg.Column(current_column), sg.VerticalSeparator(), sg.Column(previous_column)],
]
-window = sg.Window('DOMlogo', layout, location=(1000,0), keep_on_top=True)
+window = sg.Window('DOMlogo', layout, location=(600,50), keep_on_top=True, no_titlebar=True)
with open('etc/restapi.secret', 'r') as secrets:
while True:
@@ -36,11 +104,72 @@
break
print(f'Using {api_url} as endpoint.')
-contests = requests.get(f'{api_url}/contests', auth=(user,passwd)).json()
-latest_contest = sorted(contests, key=lambda c: c['end_time'])[-1]
-cid = latest_contest['id']
-api_url = f'{api_url}/contests/{cid}'
-print(f'Contest is {cid}.')
+print('Loading teams and organizations from API')
+teams = {team['id']: team for team in requests.get(f'{api_url}/teams', auth=(user, passwd)).json()}
+for team_id in teams:
+ if 'display_name' not in teams[team_id]:
+ teams[team_id]['display_name'] = teams[team_id]['name']
+organizations = {org['id']: org for org in requests.get(f'{api_url}/organizations', auth=(user, passwd)).json()}
+
+print('Downloading any new or changed logos and photos...')
+for organization in organizations.values():
+ if 'logo' in organization:
+ organization_id = organization['id']
+ logo = organization['logo'][0]
+ downloaded, downloaded_to, etag_file, etag = download_image('logo', organization_id, logo)
+ if downloaded_to:
+ # Convert to both 64x64 (for sidebar) and 160x160 (for overlay over photo)
+ downloaded_to_escaped = shlex.quote(downloaded_to)
+ target = shlex.quote(f'domlogo-files/logos/{organization_id}.png')
+ command = f'convert {downloaded_to_escaped} -resize 64x64 -background none -gravity center -extent 64x64 {target}'
+ os.system(command)
+
+ target = shlex.quote(f'domlogo-files/logos/{organization_id}.160.png')
+ command = f'convert {downloaded_to_escaped} -resize 160x160 -background none -gravity center -extent 160x160 {target}'
+ os.system(command)
+
+ with open(etag_file, 'w') as f:
+ f.write(etag)
+
+ os.unlink(downloaded_to)
+
+for team in teams.values():
+ if 'photo' in team and team['display_name'] != 'DOMjudge':
+ team_id = team['id']
+ photo = team['photo'][0]
+ downloaded, downloaded_to, etag_file, etag = download_image('photo', team_id, photo)
+ if downloaded_to:
+ # First convert to a good known size because adding the annotation and logo assumes this
+ intermediate_target = f'domlogo-files/photos/{team_id}-intermediate.png'
+ command = f'convert {downloaded_to} -resize 1024x1024 -gravity center {intermediate_target}'
+ os.system(command)
+
+ # Now add logo and team name. We use subprocess.run here to escape the team name
+ target = f'domlogo-files/photos/{team_id}.png'
+ organization_id = team['organization_id']
+ logo_file = f'domlogo-files/logos/{organization_id}.png'
+ command = [
+ 'convert',
+ intermediate_target,
+ '-fill', 'white',
+ '-undercolor', '#00000080',
+ '-gravity', 'south',
+ '-font', 'Ubuntu',
+ '-pointsize', '30',
+ '-annotate', '+5+5', f' {team["display_name"]} ',
+ logo_file,
+ '-gravity', 'northeast',
+ '-composite',
+ target
+ ]
+
+ subprocess.run(command)
+
+ with open(etag_file, 'w') as f:
+ f.write(etag)
+
+ os.unlink(downloaded_to)
+ os.unlink(intermediate_target)
latest_logfile = max(glob.glob('output/log/judge.*-2.log'), key=os.path.getctime)
print(f'Checking logfile {latest_logfile}')
@@ -50,13 +179,15 @@
results = []
last_seen, needs_update = (None, None)
while True:
- event, values = window.read(timeout=30)
- if event == sg.WIN_CLOSED:
- break
+ if needs_update is None:
+ event, values = window.read(timeout=1)
+ if event == sg.WIN_CLOSED:
+ break
line = logfile.readline()
# Sleep here for a tiny amount of time to avoid using too much CPU.
- if len(line) == 0:
- time.sleep(0.01)
+ if len(line) == 0 and needs_update is None:
+ time.sleep(0.1)
+ needs_update = None
if 'Working directory:' in line:
token = line.strip().split('/')
judging_id = token[-1]
@@ -69,8 +200,8 @@
team_id = submission_data['team_id']
last_seen = (submission_id, judging_id, team_id)
new_filename = f'domlogo-files/photos/{team_id}.png'
- if (int)(team_id) >= 120:
- new_filename = f'domlogo-files/photos/crew.png'
+ if not os.path.isfile(new_filename):
+ generate_placeholder(f'team {team_id}', new_filename, (137,28,28), 1024, 768)
team_image.update(filename=new_filename)
metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
results_text.update('Busy compiling.')
@@ -89,7 +220,6 @@
'.{1,78}', ' '.join(results))))
if needs_update:
sid, jid, tid = needs_update
- needs_update = None
judging_data = requests.get(f'{api_url}/judgements/{jid}', auth=(user,passwd)).json()
verdict = judging_data['judgement_type_id'] or 'pending'
color = 'firebrick1'
@@ -99,9 +229,16 @@
color = 'DeepSkyBlue'
for i in range(len(cache)-1):
cache[i] = cache[i+1]
- if (int)(tid) >= 120:
- tid = 'DOMjudge'
- cache[-1] = (f'domlogo-files/logos/{tid}.png', f's{sid}/j{jid}\n{verdict}', color, jid)
+ organization_id = None
+ if tid in teams:
+ organization_id = teams[tid]['organization_id']
+ organization_logo = 'domlogo-files/logos/DOMjudge.png'
+ # Organization ID is null for internal teams so explicitly check for it
+ if organization_id:
+ potential_organization_logo = f'domlogo-files/logos/{organization_id}.png'
+ if os.path.isfile(potential_organization_logo):
+ organization_logo = potential_organization_logo
+ cache[-1] = (organization_logo, f's{sid}/j{jid}\n{verdict}', color, jid)
for i in range(len(cache)):
previous_column[i][0].update(filename=cache[i][0])
previous_column[i][1].update(cache[i][1])
diff --git a/end-of-contest/README.md b/end-of-contest/README.md
new file mode 100644
index 00000000..036142c0
--- /dev/null
+++ b/end-of-contest/README.md
@@ -0,0 +1,81 @@
+## DOMjudge EOC ansible helper
+This ansible playbook should automate most of the tasks DOMjudge needs to perform for the End Of Contest procedures during the ICPC WF's. It was created for the WF 2021 in Dhaka, which took place in 2022.
+
+Two types of authentication are used and supported. Session based auth and basic auth.
+ - Session based auth retrieves a CSRF token and fakes a login action.
+ - Basic auth headers are constructed and used. This is to fully mitigate [annoying basic-auth issues](https://askubuntu.com/questions/1070838/why-wget-does-not-use-username-and-password-in-url-first-time/1070847#1070847), even though the `force_basic_auth` option should already prevent these issues from arising.
+
+The full playbook can be run using the following command. See the setup section to see what the contents of facts.yml are supposed to be.
+
+```ansible-playbook main.yml --extra-vars "@facts.yml" --tags all```
+
+### Steps automated.
+The following steps from the EOC 2022-Oct document have been automated:
+
+29. `Primary pushes results.tsv into git repo`. Tag for just this action: `results.tsv`. Note, a manual commit is still needed!
+32. `Compare scoreboard.json between primary and shadow`. Tag for just this action: `scoreboard`. Note, only if other (shadow or primary) credentials are provided.
+33. `Compare awards.json between primary and CDS`. Tag for just this action: `awards`. Note, only if CDS credentials are provided.
+36. `Verify that Primary and ... System Test`. Tag for just this action: `fetch`. Note, a manual commit is still needed. The static scoreboard (zip) and an export of the clarifications is also pulled from DOMjudge.
+
+
+## Setup
+To run this playbook a facts-file is required. An example of which can be seen below. Facts prefixed with:
+ - `dj_` are for interaction with a DOMjudge instance.
+ - `other_` are for interaction with another CLICS spec compliant CCS (commonly shadow if DOMjudge is primary, or vica versa)
+ - `cds_` are for interaction with the CDS (also CLICS compliant).
+
+Only the `repo`, `contest`, `contest_id`, and `dj_*` variables are required. When the `other_` or `cds_` facts are missing, steps which would interact with these systems are skipped.
+
+- `repo` must point to where all retrieved files should be stored, not simply the repo root!
+- `contest` must be the externalID.
+- `contest_id` must be the internalID. (cID)
+- `jd_loc` must point to the location of a [`jd` binary](https://github.com/josephburnett/jd/releases/tag/v1.6.1). Used for comparing diffs.
+- `contestutil_loc` must point to the location of [`contestUtil.sh`](https://github.com/icpctools/icpctools/releases). Used for comparing diffs.
+
+```yaml
+repo: /home/mart/icpc/wf_repo
+contest: bapc2022
+contest_id: 2
+jd_loc: /home/mart/icpc/EOC/jd
+contestutil_loc: /home/mart/Downloads/contestUtil/contestUtil.sh
+
+dj_url: https://judge.gehack.nl
+dj_url_api_suffix: api/v4
+dj_username: mart
+dj_password: REDACTED
+
+other_url: https://judge.gehack.nl
+other_url_api_suffix: api/v4
+other_username: mart
+other_password: REDACTED
+
+cds_url: https://cds.gehack.nl
+cds_url_api_suffix: api
+cds_username: admin
+cds_password: REDACTED
+```
+
+## Selectively running the playbook
+The playbook has multiple tags:
+ - `all`: runs all tasks
+ - `check`: Checks the consistency of both scoreboard.json and awards.json against the CDS and other CCS when configured.
+ - `scoreboard`: Verifies scoreboard.json against the CDS and other CCS when configured.
+ - `awards`: Verifies awards.json against the CDS and other CCS when configured.
+ - `results.tsv`: Downloads results.tsv from DOMjudge and stores it in the repo.
+ - `fetch`: Retrieves all files required by the EOC including results.tsv
+ - `dump`: Dumps all API endpoints, specifically:
+ - accounts
+ - awards
+ - balloons
+ - clarifications
+ - problems
+ - groups
+ - judgements
+ - languages
+ - organizations
+ - runs
+ - scoreboard
+ - teams
+
+### Known limitations
+ - The checks that verify whether the awards.json are the same is *very* simple and I (Mart) have not yet found a reasonable way of actually checking them in a nice manner from ansible. Them matching would be a bigger red-flag than them having a difference. Still, the playbook fails when a difference is detected! To aid with (manual) verification the results of fetching the jsons is stored in `/tmp/awards.[sys].json` with `[sys]` either "dj", or "cds".
diff --git a/end-of-contest/main.yml b/end-of-contest/main.yml
new file mode 100644
index 00000000..750a12ba
--- /dev/null
+++ b/end-of-contest/main.yml
@@ -0,0 +1,237 @@
+---
+- hosts: localhost
+ connection: local
+ gather_facts: no
+ tasks:
+ - name: Set facts for whether enough variables are set
+ tags: all, compare, scoreboard, awards, fetch
+ ansible.builtin.set_fact:
+ cds_available: "{{ cds_url is defined and cds_url_api_suffix is defined and cds_username is defined and cds_password is defined }}"
+ other_available: "{{ other_url is defined and other_url_api_suffix is defined and other_username is defined and other_password is defined }}"
+
+ # Do this to prevent the dreaded issues with basic auth. Tedious but it works
+ - name: Set DOMjudge basic auth fact
+ tags: all, compare, scoreboard, awards, fetch, dump
+ ansible.builtin.set_fact:
+ dj_basic_auth: "Basic {{ (dj_username+':'+dj_password) | b64encode }}"
+
+ - name: Set CDS basic auth fact
+ tags: all, compare, scoreboard, awards
+ ansible.builtin.set_fact:
+ cds_basic_auth: "Basic {{ (cds_username+':'+cds_password) | b64encode }}"
+ when: cds_available
+
+ - name: Set other basic auth fact
+ tags: all, compare, scoreboard, awards
+ ansible.builtin.set_fact:
+ other_basic_auth: "Basic {{ (other_username+':'+other_password) | b64encode }}"
+ when: other_available
+
+ - name: Attempt login into DOMjudge
+ tags: all, fetch, results.tsv
+ block:
+ - name: Fetch CSRF from login page
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "{{ dj_url }}/login"
+ register: DOMjudge_login_output
+
+ - name: Extract CSRF input field
+ ansible.builtin.set_fact:
+ DOMjudge_csrf_field: "{{ DOMjudge_login_output.content | regex_search(' ]+?_csrf_token[^>]+?>') | regex_search('[^\"]{30,}') }}"
+
+ - name: Login into DOMjudge and retrieve cookie
+ ansible.builtin.uri:
+ method: POST
+ body_format: form-urlencoded
+ url: "{{ dj_url }}/login"
+ status_code: 302
+ headers:
+ cookie: "{{ DOMjudge_login_output.cookies_string }}"
+ body:
+ _username: "{{ dj_username }}"
+ _password: "{{ dj_password }}"
+ _csrf_token: "{{ DOMjudge_csrf_field }}"
+ register: login_response
+
+ - name: Test succesfull login by going to a restricted page
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/jury"
+ method: GET
+ headers:
+ cookie: "{{ login_response.set_cookie }}"
+
+ - name: Download results.tsv
+ tags: all, results.tsv
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/jury/import-export/export/results.tsv"
+ return_content: yes
+ method: GET
+ creates: "{{ repo }}/results.tsv"
+ dest: "{{ repo }}/results.tsv"
+ headers:
+ cookie: "{{ login_response.set_cookie }}"
+
+ - name: Compare scoreboard.json
+ tags: all, compare, scoreboard
+ block:
+ - name: "Fetch scoreboard.json from Dj"
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/contests/{{ contest }}/scoreboard"
+ dest: "/tmp/scoreboard.dj.json"
+ return_content: yes
+ force: yes
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+ register: dj_scoreboard
+
+ - name: "Fetch scoreboard.json from other"
+ ansible.builtin.uri:
+ url: "{{ other_url }}/{{ other_url_api_suffix }}/contests/{{ contest }}/scoreboard"
+ dest: "/tmp/scoreboard.other.json"
+ return_content: yes
+ force: yes
+ headers:
+ Authorization: "{{ other_basic_auth }}"
+ register: other_scoreboard
+ when: other_available
+
+ - name: Does Dj == other for scoreboard
+ when: other_available
+ block:
+ - name: Check for differences
+ shell: "{{ contestutil_loc }} ScoreboardUtil /tmp/scoreboard.dj.json /tmp/scoreboard.other.json"
+ when: other_available
+ register: cul
+
+ - name: Print output
+ ansible.builtin.debug:
+ msg: "{{ cul.stdout | split('\n')}}"
+
+ - name: Fail if the scoreboard does not match
+ ansible.builtin.fail:
+ msg: "Scoreboard does not match, check /tmp/scoreboard.dj.json and /tmp/scoreboard.other.json"
+ when:
+ - "'No differences found.' not in cul.stdout"
+
+ - name: Compare awards.json
+ tags: all, compare, awards
+ block:
+ - name: Fetch awards.json from Dj
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/contests/{{ contest }}/awards/"
+ dest: "/tmp/awards.dj.json"
+ return_content: yes
+ force: yes
+ method: GET
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+ register: dj_awards
+
+ - name: Fetch awards.json from cds
+ ansible.builtin.uri:
+ url: "{{ cds_url }}/{{ cds_url_api_suffix }}/contests/{{ contest }}/awards/"
+ dest: "/tmp/awards.cds.json"
+ return_content: yes
+ force: yes
+ method: GET
+ headers:
+ Authorization: "{{ cds_basic_auth }}"
+ register: cds_awards
+ when: cds_available
+
+ - name: Does Dj == CDS for awards
+ shell: "{{ jd_loc }} /tmp/awards.dj.json /tmp/awards.cds.json"
+ when: cds_available
+
+ - name: "Fetch event-feed.ndjson from Dj"
+ tags: all, fetch
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/contests/{{ contest }}/event-feed?stream=false"
+ return_content: yes
+ method: GET
+ creates: "{{ repo }}/event-feed.ndjson"
+ dest: "{{ repo }}/event-feed.ndjson"
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+
+ - name: "Fetch {{ item }}-endpoint from Dj"
+ tags: all, fetch, dump
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/contests/{{ contest }}/{{ item }}"
+ return_content: yes
+ method: GET
+ creates: "{{ repo }}/{{ item }}.json"
+ dest: "{{ repo }}/{{ item }}.json"
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+ with_items:
+ - accounts
+ - awards
+ - balloons
+ - clarifications
+ - problems
+ - groups
+ - judgements
+ - languages
+ - organizations
+ - runs
+ - scoreboard
+ - teams
+
+ - name: "Store final_standings.html (results.html)"
+ tags: all, fetch
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/jury/import-export/export/results.html"
+ creates: "{{ repo }}/final_standings.html"
+ dest: "{{ repo }}/final_standings.html"
+ headers:
+ cookie: "{{ login_response.set_cookie }}"
+
+ - name: "Store (clarifications.html)"
+ tags: all, fetch
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/jury/import-export/export/clarifications.html"
+ creates: "{{ repo }}/clarifications.html"
+ dest: "{{ repo }}/clarifications.html"
+ headers:
+ cookie: "{{ login_response.set_cookie }}"
+
+ - name: "Store final scoreboard (contest.zip)"
+ tags: all, fetch
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/public/scoreboard-data.zip?contest={{ contest_id }}"
+ creates: "{{ repo }}/final_scoreboard.zip"
+ dest: "{{ repo }}/final_scoreboard.zip"
+ headers:
+ cookie: "{{ login_response.set_cookie }}"
+
+ - name: "Fetch DOMjudge submissions"
+ tags: all, fetch
+ block:
+ - name: Retrieve all submissions
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/contests/{{ contest }}/submissions?strict=1"
+ return_content: yes
+ method: GET
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+ register: "submissions"
+
+ - name: Extract submission endpoints
+ ansible.builtin.set_fact:
+ submissions: "{{ submissions.json | json_query(jmesquery) }}"
+ vars:
+ jmesquery: "[].files[?mime == 'application/zip'][]"
+
+ - name: Download submissions
+ ansible.builtin.uri:
+ url: "{{ dj_url }}/{{ dj_url_api_suffix }}/{{ item.href }}"
+ return_content: yes
+ method: GET
+ headers:
+ Authorization: "{{ dj_basic_auth }}"
+ creates: "{{ repo }}/{{ item.href.split('/')[3] }}.zip"
+ dest: "{{ repo }}/{{ item.href.split('/')[3] }}.zip"
+ loop: "{{ submissions }}"
diff --git a/merchandise/DOMjudgelogo+site.pdf b/merchandise/DOMjudgelogo+site.pdf
new file mode 100644
index 00000000..0ba270ec
Binary files /dev/null and b/merchandise/DOMjudgelogo+site.pdf differ
diff --git a/merchandise/DOMjudgelogo-blue-bg.png b/merchandise/DOMjudgelogo-blue-bg.png
new file mode 100644
index 00000000..442e6821
Binary files /dev/null and b/merchandise/DOMjudgelogo-blue-bg.png differ
diff --git a/merchandise/DOMjudgelogo-blue-bg.svg b/merchandise/DOMjudgelogo-blue-bg.svg
new file mode 100644
index 00000000..5ec3e172
--- /dev/null
+++ b/merchandise/DOMjudgelogo-blue-bg.svg
@@ -0,0 +1,242 @@
+
+image/svg+xml DOM
+DOM
+judge
+judge
+/
+/
+\
+\
+
diff --git a/merchandise/DOMjudgelogo.svg b/merchandise/DOMjudgelogo.svg
new file mode 120000
index 00000000..23bb28b4
--- /dev/null
+++ b/merchandise/DOMjudgelogo.svg
@@ -0,0 +1 @@
+../website/DOMjudgelogo.svg
\ No newline at end of file
diff --git a/new_release_howto.md b/new_release_howto.md
index 1994c8a1..16b2d356 100644
--- a/new_release_howto.md
+++ b/new_release_howto.md
@@ -4,6 +4,8 @@ on the account `domjudge@vm-domjudge`):
1. Test everything. (duh...)
1. Commit the correct version number in the `ChangeLog` and `README` files.
+ 1. Discuss if the current second to last minor version will now be EOL.
+ 1. Update https://github.com/DOMjudge/domjudge/security/policy to support 2 minor versions, including the one now being released.
1. Change the constants `webapp/src/Controller/API/GeneralInfoController.php`
to point to the correct CCS API version.
1. `export TAG=x.y.z`
@@ -28,10 +30,11 @@ on the account `domjudge@vm-domjudge`):
1. On the server the tarball will be built, signed and published.
1. Update the DOMjudge homepage: commit changes in the `domjudge-scripts`
repository under `website/` and run `make install` as domjudge@domjudge
+ 1. Remove unsupported versions from the `versions.json` in the next step.
1. If this is a new major or minor version, update the release documentation
under `/srv/http/domjudge/docs/manual` by adding a new version to the
- file `versions.json` and updating the redirect destination in `index.html`.
- The documentation is regenerated once every hour.
+ file `versions.json` and updating the redirect destination in `index.html`
+ and `team.html`. The documentation is regenerated once every hour.
1. Bump the Docker images by starting a new pipeline
[here](https://gitlab.com/DOMjudge/domjudge-packaging/-/pipelines/new) and
setting `DOMJUDGE_VERSION` to the version to release. Set `DOMJUDGE_LATEST`
@@ -39,9 +42,9 @@ on the account `domjudge@vm-domjudge`):
`Run pipeline`, make sure to click the *play* button next to `release-DOMjudge`
to actually build and push the Docker images. You can view the progress by
clicking on the job.
- 1. Build Debian packages (or make someone
- do this).
+ 1. Update https://gitlab.com/DOMjudge/domjudge-packaging/-/pipeline\_schedules to the latest version.
+ 1. Build Debian packages (or make someone do this).
1. Put debian packages in `/srv/http/domjudge/debian/mini-dinstall/incoming`
and run as domjudge@domjudge: `mini-dinstall -b`
- 1. Send an email to `domjudge-announce@domjudge.org`.
+ 1. Send an email to `domjudge-announce@domjudge.org`, announcing the new version and state which versions are supported.
1. Add the released branch to the dependabot.yml (https://github.com/DOMjudge/domjudge/blob/main/.github/dependabot.yml)
diff --git a/provision-contest/ansible/.gitignore b/provision-contest/ansible/.gitignore
new file mode 100644
index 00000000..6ff331c7
--- /dev/null
+++ b/provision-contest/ansible/.gitignore
@@ -0,0 +1 @@
+hosts
diff --git a/provision-contest/ansible/Makefile b/provision-contest/ansible/Makefile
index 85365fcf..06f39de7 100644
--- a/provision-contest/ansible/Makefile
+++ b/provision-contest/ansible/Makefile
@@ -6,45 +6,95 @@ default:
@echo " - make admin"
@echo " - make grafana"
@echo " - make cds"
+ @echo " - make presclient"
+ @echo " - make presadmin"
@echo " - make scoreboard"
+ @echo " - make autoanalyst"
-LIBVENDORTGZ=roles/domjudge_checkout/files/lib-vendor.tgz
-SSHKEY=roles/ssh/files/id_rsa
+VENDORTGZ=roles/domjudge_checkout/files/vendor.tgz
+SSHKEY=roles/ssh/files/id_ed25519
+SSL_DOMSERVER=roles/ssl/files/domserver
+SSL_DOMSERVER_FILES=$(addprefix $(SSL_DOMSERVER),.key .crt)
+SSL_NODEEXPORT=roles/prometheus_target_all/files/node_exporter
+SSL_NODEEXPORT_FILES=$(addprefix $(SSL_NODEEXPORT),.key .crt)
SSL_LOCALHOST=roles/ssl/files/localhost
SSL_LOCALHOST_FILES=$(addprefix $(SSL_LOCALHOST),.key .crt)
+SSL_CDS=roles/ssl/files/cds
+SSL_CDS_FILES=$(addprefix $(SSL_CDS),.key .crt)
SSL_GRAFANA=roles/grafana/files/ssl
SSL_GRAFANA_FILES=$(addprefix $(SSL_GRAFANA),.key .crt)
-ifeq ($(wildcard $(LIBVENDORTGZ)),)
-LIBVENDOR=
+ifeq ($(wildcard $(VENDORTGZ)),)
+VENDOR=
else
-LIBVENDOR=roles/domjudge_checkout/files/lib/vendor
-$(LIBVENDOR): $(LIBVENDORTGZ)
- -cd roles/domjudge_checkout/files && tar xzf $(notdir $<)
+VENDOR=roles/domjudge_checkout/files/webapp/vendor
+$(VENDOR): $(VENDORTGZ)
+ -cd roles/domjudge_checkout/files/webapp && tar xzf ../$(notdir $<)
endif
-domserver judgehost admin grafana cds scoreboard mgmt: %: %.yml hosts group_vars/all/secret.yml $(LIBVENDOR) $(SSHKEY) $(SSHKEY).pub
+ROLES=domserver judgehost admin grafana cds presclient presadmin scoreboard mgmt autoanalyst
+$(ROLES): %: %.yml hosts group_vars/all/secret.yml $(VENDOR) $(SSHKEY) $(SSHKEY).pub
ansible-playbook -i hosts $<
+FAILED_ROLES=$(addprefix failed-,$(ROLES))
+$(FAILED_ROLES): failed-%: %.yml %.retry
+ ansible-playbook -i hosts --limit @$*.retry $<
+
+CODEONLY_ROLES=$(addprefix codeonly-,domserver judgehost admin)
+$(CODEONLY_ROLES): codeonly-%:
+ ansible-playbook -i hosts --tags pretask,domjudge_build $*.yml
+
+powerloss:
+ ansible-playbook -i hosts emergency.yml --tags powerloss
+
+lockdown:
+ ansible-playbook -i hosts emergency.yml --tags full_lockdown
+
+lockdown-force:
+ ansible-playbook -i hosts emergency.yml --tags full_lockdown,force_lockdown
+
+ansible-master:
+ for book in $(ROLES) ; do \
+ ansible-playbook -i hosts $$book.yml --tags install_master ; \
+ done
+
admin: $(SSL_LOCALHOST_FILES)
-grafana: $(SSL_GRAFANA_FILES)
+grafana: $(SSL_GRAFANA_FILES) $(SSL_NODEEXPORT_FILES)
+domserver: $(SSL_DOMSERVER_FILES) $(SSL_NODEEXPORT_FILES)
+judgehost: $(SSL_NODEEXPORT_FILES)
+cds: $(SSL_CDS_FILES) $(SSL_NODEEXPORT_FILES)
+scoreboard: $(SSL_NODEEXPORT_FILES)
+mgmt: $(SSL_NODEEXPORT_FILES)
$(SSHKEY) $(SSHKEY).pub:
- ssh-keygen -t rsa -f $(SSHKEY) -P ''
+ ssh-keygen -t ed25519 -f $(SSHKEY) -P ''
+$(SSL_DOMSERVER_FILES):
+ openssl req -x509 -nodes -newkey rsa:4096 -subj "/O=DOMjudge/CN=domjudge" \
+ -addext "subjectAltName = DNS:wf46-domjudge,DNS:wf47-domjudge,DNS:analyst" \
+ -sha256 -days 365 -keyout $(SSL_DOMSERVER).key -out $(SSL_DOMSERVER).crt
+$(SSL_NODEEXPORT_FILES):
+ openssl req -x509 -nodes -newkey rsa:4096 -subj "/O=DOMjudge/CN=metricexporter" \
+ -sha256 -days 365 -keyout $(SSL_NODEEXPORT).key -out $(SSL_NODEEXPORT).crt
$(SSL_LOCALHOST_FILES):
openssl req -x509 -nodes -newkey rsa:4096 -subj "/O=DOMjudge/CN=localhost" \
-sha256 -days 365 -keyout $(SSL_LOCALHOST).key -out $(SSL_LOCALHOST).crt
+$(SSL_CDS_FILES):
+ openssl req -x509 -nodes -newkey rsa:4096 -subj "/O=DOMjudge/CN=cds" \
+ -sha256 -days 365 -keyout $(SSL_CDS).key -out $(SSL_CDS).crt
$(SSL_GRAFANA_FILES):
openssl req -x509 -nodes -newkey rsa:4096 -subj "/O=DOMjudge/CN=grafana" \
-sha256 -days 365 -keyout $(SSL_GRAFANA).key -out $(SSL_GRAFANA).crt
clean:
- rm -rf $(LIBVENDOR)
+ rm -rf $(VENDOR)
distclean: clean
rm -f $(SSHKEY) $(SSHKEY).pub
+ rm -f $(SSL_DOMSERVER_FILES)
+ rm -f $(SSL_NODEEXPORT_FILES)
rm -f $(SSL_LOCALHOST_FILES)
+ rm -f $(SSL_CDS_FILES)
rm -f $(SSL_GRAFANA_FILES)
-.PHONY: default clean distclean domserver judgehost admin grafana cds scoreboard
+.PHONY: default clean distclean $(ROLES) $(FAILED_ROLES) $(CODEONLY_ROLES) powerloss lockdown lockdown-force
diff --git a/provision-contest/ansible/README.md b/provision-contest/ansible/README.md
index 0dcb3631..b8493c8d 100644
--- a/provision-contest/ansible/README.md
+++ b/provision-contest/ansible/README.md
@@ -13,7 +13,15 @@ This ansible code can deploy four different types of machines:
* judgehost: machines running one judgedaemon each. We use as many as
necessary and depending on machines available. These should be
identical to each other and preferably also to the team machines.
-* grafana: TODO
+* scoreboard: for some contests (different from the world finals) we
+ use a mirror of the scoreboard to not overload the server with
+ requests from viewers.
+* cds: machine which hosts the [CDS](https://tools.icpc.global/cds/), if you use
+ this you should also look at the `presclient` & `presadmin` playbooks to setup
+ machines which can present those presentations.
+* grafana: monitoring machine to observe the performance of both the DOMjudge instance
+ and the webservers, optionally we monitor the participant machines in case of suspected
+ hardware issues (loose PSU, CPU performance etc.)
## Code organization
@@ -24,13 +32,19 @@ to prevent duplication.
Global and group variables are stored under `group_vars`. The file
`group_vars/all/secret.yml.example` should be copied to
`group_vars/all/secret.yml` and then all variables should be set
-and/or modified as required.
+and/or modified as required. The script `generate_passwords.py` can be used
+to prefill some of those passwords with a xkcd style password. In `secret.yml.example`
+the passwords can be listed as either `{some-password}` or `some-password` the `{}` is *not*
+required but only used as anchor for the script, so adding the `{}` would make it part of the
+password.
There are a few places where additional files should/can be added:
* SSH public/private keys under `roles/ssh/files/`.
* SSL certificates and keys under `roles/ssl/files/`.
* Machine/group specific local packages under `roles/base_packages/files/install-*/`.
* Judgehost chroot local packages under `roles/judgedaemon/files/install-chroot/`.
+* The vendor dependencies under `roles/domjudge_checkout/files/vendor.tgz`.
+* Machine/group specific docker containers under `roles/docker/files/containers-*/`.
## TODO
diff --git a/provision-contest/ansible/admin.yml b/provision-contest/ansible/admin.yml
index c7419c70..39182080 100644
--- a/provision-contest/ansible/admin.yml
+++ b/provision-contest/ansible/admin.yml
@@ -1,7 +1,7 @@
---
# This playbook an admin machine with a dev DOMjudge instance
-- name: setup admin machines
+- name: Setup admin machines
hosts: admin
vars:
host_type: admin
@@ -30,6 +30,8 @@
tags: domjudge_checkout
- role: domjudge_build
tags: domjudge_build
+ vars:
+ DOMSERVER: https://localhost
- role: domserver
tags: domserver
- role: judgedaemon
@@ -38,75 +40,55 @@
tags: clusterssh
- role: phpstorm
tags: phpstorm
+ - role: dj_notify
+ tags: dj_notify
- role: prometheus_target_all
tags: prometheus_target_all
when: GRAFANA_MONITORING
handlers:
- - include: handlers.yml
+ - import_tasks: handlers.yml
tasks:
- - name: install common required/useful packages
+ - name: Install common required/useful packages
tags: packages
apt:
state: present
pkg:
- - gitk
+ - ansible
- git-gui
+ - gitk
- makepasswd
- mmv
+ - python3-pdfkit
+ - xkcdpass
- - name: enable developer mode
+ - name: Enable developer mode
lineinfile:
regexp: 'APP_ENV='
line: 'APP_ENV=dev'
dest: "{{ DJ_DIR }}/webapp/.env.local"
-
- - name: add cds to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: 'cds$'
- line: "10.3.3.207 cds"
-
- - name: add kattis to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: 'kattis$'
- line: "10.3.3.212 kattis"
-
- - name: add domjudge-laptop to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: 'domjudge-laptop$'
- line: "10.3.3.200 domjudge-laptop"
-
- - name: add nisprint to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: 'printsrv$'
- line: "10.3.3.211 nisprint nismaster printsrv"
+ create: true
- name: Update repo URL based on network
set_fact:
dj_git_repo_scripts: "{{ DJ_GIT_REPO_SCRIPTS_RESTRICTED if WF_RESTRICTED_NETWORK else DJ_GIT_REPO_SCRIPTS }}"
- - name: create working copy of the domjudge-scripts repo
- become: true
- become_user: domjudge
- # We use a different directory here to have one single 'upstream' and not have issues with it
- git: repo={{ dj_git_repo_scripts }} dest=/home/domjudge/domjudge-scripts-checkout version=main accept_hostkey=yes update=no
-
- - name: create working copy of the wf2020 repo
- become: true
- become_user: domjudge
- git: repo=git@cds:wf2020 dest=/home/domjudge/wf2020 version=master accept_hostkey=yes update=no
-
- - name: copy custom CSS file for admin machines
+ - name: Copy custom CSS file for admin machines
copy:
src: admin-machine.css
dest: "{{ DJ_DIR }}/webapp/public/css/custom/admin-machine.css"
owner: domjudge
group: domjudge
mode: 0644
- notify: clear application cache
+ notify: Clear application cache
+
+ - name: Check if netrc file exists
+ stat:
+ path: /home/domjudge/.netrc
+ get_attributes: false
+ get_checksum: false
+ get_mime: false
+ register: netrc_file
+ check_mode: no
- name: Install netrc file
template:
@@ -115,3 +97,75 @@
owner: domjudge
group: domjudge
mode: 0600
+ when: not netrc_file.stat.exists
+
+ - name: Install .my.cnf file
+ template:
+ src: files/admin.my.cnf.j2
+ dest: /home/domjudge/.my.cnf
+ owner: domjudge
+ group: domjudge
+ mode: 0600
+
+ - name: Create local python directory
+ become: true
+ become_user: domjudge
+ file:
+ path: "/home/domjudge/.local/lib/python3.8"
+ state: directory
+ mode: '0755'
+ owner: domjudge
+ group: domjudge
+
+
+ - name: Install local python packages
+ synchronize:
+ src: files/admin-python/
+ dest: "/home/domjudge/.local/lib/python3.8"
+ owner: false
+ use_ssh_args: true
+
+ - name: Fix ownership of local python packages
+ file:
+ path: "/home/domjudge/.local/lib/python3.8"
+ recurse: true
+ owner: domjudge
+ group: domjudge
+
+ - name: Grant www-data permissions to user homedir (needed for web browser)
+ acl:
+ path: "/home/domjudge"
+ entity: www-data
+ etype: user
+ permissions: rwx
+ state: present
+
+ - name: Download offline repos
+ when: WF_RESTRICTED_NETWORK
+ become: true
+ become_user: domjudge
+ block:
+ - name: Add ccsadmin remote
+ git_config:
+ name: "remote.{{ item.remote }}.url"
+ value: "{{ item.url }}"
+ scope: local
+ repo: /home/domjudge/domjudge-checkout
+ loop:
+ - remote: origin
+ url: ansible@10.3.3.224:domjudge
+ - remote: github
+ url: git@github.com:DOMjudge/domjudge.git
+ - remote: sysops
+ url: domjudge@packages:domjudge.git
+ - remote: initial
+ url: ansible@domjudge-ccsadmin1:domjudge.git
+
+ - name: Create working copy of the domjudge-scripts repo
+ # We use a different directory here to have one single 'upstream' and not have issues with it
+ git: repo={{ dj_git_repo_scripts }} dest=/home/domjudge/domjudge-scripts-checkout version=main accept_hostkey=yes update=no
+
+ - name: Create working copy of the contest repo
+ ignore_errors: true
+ git: repo=git@packages:{{ CONTEST_REPO }} dest=/home/domjudge/{{ CONTEST_REPO }} version=master accept_hostkey=yes update=no
+
diff --git a/provision-contest/ansible/ansible.cfg b/provision-contest/ansible/ansible.cfg
new file mode 100644
index 00000000..ee6bd190
--- /dev/null
+++ b/provision-contest/ansible/ansible.cfg
@@ -0,0 +1,11 @@
+[defaults]
+forks = 10
+log_path = /tmp/ansible.log
+system_warnings = True
+strategy = free
+retry_files_enabled = True
+retry_files_save_path = ~/.ansible-retry
+
+[connection]
+ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
+pipelining = True
diff --git a/provision-contest/ansible/autoanalyst.yml b/provision-contest/ansible/autoanalyst.yml
new file mode 100644
index 00000000..1a411419
--- /dev/null
+++ b/provision-contest/ansible/autoanalyst.yml
@@ -0,0 +1,9 @@
+---
+# This playbook installs the ICPC AutoAnalyst
+
+- name: Setup autoanalyst
+ hosts: autoanalyst
+ become: true
+ roles:
+ - role: autoanalyst
+ tags: autoanalyst
diff --git a/provision-contest/ansible/cds.yml b/provision-contest/ansible/cds.yml
index fa9b45cd..ae4cb730 100644
--- a/provision-contest/ansible/cds.yml
+++ b/provision-contest/ansible/cds.yml
@@ -1,11 +1,13 @@
---
# This playbook installs the CDS
-- name: setup CDS
+- name: Setup CDS
hosts: cds
vars:
host_type: cds
become: true
+ handlers:
+ - import_tasks: handlers.yml
roles:
- role: base_packages
tags: base_packages
diff --git a/provision-contest/ansible/domserver.yml b/provision-contest/ansible/domserver.yml
index e1c5b75d..c688690c 100644
--- a/provision-contest/ansible/domserver.yml
+++ b/provision-contest/ansible/domserver.yml
@@ -1,8 +1,19 @@
---
# This playbook installs the DOMjudge server(s)
-- name: setup domserver
- hosts: domserver
+- name: Install master dependency for ipaddr jinja filter
+ hosts: localhost
+ tasks:
+ - name: Install for debian family
+ when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
+ tags: install_master,never
+ apt:
+ state: present
+ pkg:
+ - python3-netaddr
+
+- name: Setup domserver
+ hosts: domserver,online-domserver
vars:
host_type: domserver
become: true
@@ -48,9 +59,9 @@
tags: prometheus_target_all
when: GRAFANA_MONITORING
handlers:
- - include: handlers.yml
+ - import_tasks: handlers.yml
tasks:
- - name: install domserver required packages
+ - name: Install domserver required packages
apt:
state: present
pkg:
@@ -58,19 +69,32 @@
- macchanger
- molly-guard
- - name: disable developer mode
+ - name: Disable developer mode
lineinfile:
regexp: '^APP_ENV=dev'
state: absent
dest: "{{ DJ_DIR }}/webapp/.env.local"
- - name: install contest images
+ - name: Install contest images
synchronize:
src: files/domjudge-public/
dest: "{{ DJ_DIR }}/webapp/public"
owner: false
use_ssh_args: true
- notify: clear application cache
+ notify: Clear application cache
+
+ - name: Grant www-data permissions to image folders
+ acl:
+ path: "{{ DJ_DIR }}/webapp/public/images/{{ item }}"
+ entity: www-data
+ etype: user
+ permissions: rwx
+ state: present
+ recursive: true
+ loop:
+ - teams
+ - affiliations
+ - banners
- name: Add documentation in DOMjudge team interface
copy:
@@ -79,9 +103,17 @@
mode: 0644
group: root
owner: root
- notify: clear application cache
+ notify: Clear application cache
+
+ - name: Add log viewing helper script
+ copy:
+ src: files/nicelog
+ dest: /home/domjudge/bin
+ owner: domjudge
+ group: domjudge
+ mode: 0755
- - name: add autostart shortcuts
+ - name: Add autostart shortcuts
copy:
src: files/{{ item }}.desktop
dest: /home/domjudge/.config/autostart/
@@ -91,8 +123,9 @@
loop:
- htop
- taillog-domserver-nginx-error
+ when: GRAPHICAL
- - name: add autostart shortcuts from template
+ - name: Add autostart shortcuts from template
template:
src: files/{{ item }}.desktop.j2
dest: /home/domjudge/.config/autostart/{{ item }}.desktop
@@ -101,3 +134,4 @@
mode: 0755
loop:
- taillog-domserver-symfony-error
+ when: GRAPHICAL
diff --git a/provision-contest/ansible/emergency.yml b/provision-contest/ansible/emergency.yml
new file mode 100644
index 00000000..e2b7b28f
--- /dev/null
+++ b/provision-contest/ansible/emergency.yml
@@ -0,0 +1,60 @@
+---
+- hosts: domserver
+ tasks:
+ - name: Disable webserver to stop submissions
+ tags: powerloss
+ service:
+ name: nginx
+ state: stopped
+
+- hosts: all
+ tasks:
+ - name: Disable GDM autologin
+ tags: full_lockdown
+ lineinfile:
+ path: /etc/gdm3/custom.conf
+ regexp: 'AutomaticLoginEnable'
+ line: 'AutomaticLoginEnable=false'
+ create: true
+ mode: 0644
+ when: GRAPHICAL
+
+- hosts: judgehost
+ tasks:
+ - name: Disable all running judgedaemons
+ tags: full_lockdown
+ service:
+ name: "{{ item }}"
+ state: stopped
+ with_sequence: start=0 end={{ ansible_processor_vcpus }} format=domjudge-judgedaemon@%1x
+
+- hosts: domserver:!emergency
+ tasks:
+ - name: Disable our domserver instance
+ # We also disable the startup as we're now in most likely a bad state
+ # make sure that replication can only start when someone is looking at it
+ tags: full_lockdown
+ service:
+ name: "{{ item }}"
+ state: stopped
+ enabled: false
+ loop:
+ - nginx
+ - php7.4-fpm
+ - mysql
+
+- hosts: all:!admin
+ tasks:
+ - name: Shutdown all non-needed computers
+ tags: full_lockdown
+ community.general.shutdown:
+
+# Get current running machine
+- hosts: localhost
+
+- hosts: all
+ tasks:
+ - name: Shutdown all machines except for us
+ tags: force_lockdown
+ community.general.shutdown:
+ when: hostvars.localhost.ansible_hostname != ansible_hostname
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/INSTALLER b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/LICENSE b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/LICENSE
new file mode 100644
index 00000000..9b114e03
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Manraj Singh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/METADATA b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/METADATA
new file mode 100644
index 00000000..9c272bff
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/METADATA
@@ -0,0 +1,235 @@
+Metadata-Version: 2.1
+Name: halo
+Version: 0.0.31
+Summary: Beautiful terminal spinners in Python
+Home-page: https://github.com/manrajgrover/halo
+Author: Manraj Singh
+Author-email: manrajsinghgrover@gmail.com
+License: MIT
+Keywords: console,loading,indicator,progress,cli,spinner,spinners,terminal,term,busy,wait,idle
+Platform: UNKNOWN
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.4
+Description-Content-Type: text/markdown
+License-File: LICENSE
+Requires-Dist: colorama (>=0.3.9)
+Requires-Dist: log-symbols (>=0.0.14)
+Requires-Dist: six (>=1.12.0)
+Requires-Dist: spinners (>=0.0.24)
+Requires-Dist: termcolor (>=1.1.0)
+Requires-Dist: backports.shutil-get-terminal-size (>=1.0.0) ; python_version < "3.3"
+Provides-Extra: ipython
+Requires-Dist: IPython (==5.7.0) ; extra == 'ipython'
+Requires-Dist: ipywidgets (==7.1.0) ; extra == 'ipython'
+
+
+
+
+ halo
+
+
+[](https://travis-ci.com/manrajgrover/halo) [](https://ci.appveyor.com/project/manrajgrover/halo) [](https://coveralls.io/github/manrajgrover/halo?branch=master)
+ [](https://github.com/manrajgrover/halo)  [](https://pepy.tech/project/halo) [](https://pepy.tech/project/halo/month)
+> Beautiful spinners for terminal, IPython and Jupyter
+
+
+
+## Install
+
+```shell
+$ pip install halo
+```
+
+## Usage
+
+```py
+from halo import Halo
+
+spinner = Halo(text='Loading', spinner='dots')
+spinner.start()
+
+# Run time consuming work here
+# You can also change properties for spinner as and when you want
+
+spinner.stop()
+```
+
+Alternatively, you can use halo with Python's `with` statement:
+
+```py
+from halo import Halo
+
+with Halo(text='Loading', spinner='dots'):
+ # Run time consuming work here
+```
+
+Finally, you can use halo as a decorator:
+
+```py
+from halo import Halo
+
+@Halo(text='Loading', spinner='dots')
+def long_running_function():
+ # Run time consuming work here
+ pass
+
+long_running_function()
+```
+
+## API
+
+#### `Halo([text|text_color|spinner|animation|placement|color|interval|stream|enabled])`
+
+##### `text`
+*Type*: `str`
+
+Text shown along with spinner.
+
+##### `text_color`
+*Type*: `str`
+*Values*: `grey`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`
+
+Color of the spinner text. Defaults to `None`.
+
+##### `spinner`
+*Type*: `str|dict`
+
+If string, it should be one of the spinners listed in the given [json](https://github.com/sindresorhus/cli-spinners/blob/dac4fc6571059bb9e9bc204711e9dfe8f72e5c6f/spinners.json) file. If a dict is passed, it should define `interval` and `frames`. Something like:
+
+```py
+{
+ 'interval': 100,
+ 'frames': ['-', '+', '*', '+', '-']
+}
+```
+
+Defaults to `dots` spinner. For Windows users, it defaults to `line` spinner.
+
+##### `animation`
+*Type*: `str`
+*Values*: `bounce`, `marquee`
+
+Animation to apply to the text if it's too large and doesn't fit in the terminal. If no animation is defined, the text will be ellipsed.
+
+##### `placement`
+*Type*: `str`
+*Values*: `left`, `right`
+
+Which side of the text the spinner should be displayed. Defaults to `left`
+
+##### `color`
+*Type*: `str`
+*Values*: `grey`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`
+
+Color of the spinner. Defaults to `cyan`.
+
+##### `interval`
+*Type*: `float`
+
+Interval between each frame. Defaults to spinner interval (recommended).
+
+##### `stream`
+*Type*: `file`
+
+Stream to write the output. Defaults to `sys.stdout`.
+
+##### `enabled`
+*Type*: `bool`
+
+Enable or disable the spinner. Defaults to `True`.
+
+### Methods
+
+Following are the methods available:
+
+#### `spinner.start([text])`
+
+Starts the spinner. If `text` is passed, it is set as spinner text. Returns the instance.
+
+#### `spinner.stop()`
+
+Stops and clears the spinner. Returns the instance.
+
+#### `spinner.clear()`
+
+Clears the spinner. Returns the instance.
+
+#### `spinner.render()`
+
+Manually renders a new frame. Returns the instance.
+
+#### `spinner.frame()`
+
+Returns next frame to be rendered.
+
+#### `spinner.succeed([text])`
+##### `text`: *Type*: `str`
+
+Stops the spinner and changes symbol to `✔`. If text is provided, it is persisted else current text is persisted. Returns the instance.
+
+#### `spinner.fail([text])`
+##### `text`: *Type*: `str`
+
+Stops the spinner and changes symbol to `✖`. If text is provided, it is persisted else current text is persisted. Returns the instance.
+
+#### `spinner.warn([text])`
+##### `text`: *Type*: `str`
+
+Stops the spinner and changes symbol to `⚠`. If text is provided, it is persisted else current text is persisted. Returns the instance.
+
+#### `spinner.info([text])`
+##### `text`: *Type*: `str`
+
+Stops the spinner and changes symbol to `ℹ`. If text is provided, it is persisted else current text is persisted. Returns the instance.
+
+#### `spinner.stop_and_persist([symbol|text])`
+Stops the spinner and changes symbol and text. Returns the instance.
+
+##### `symbol`
+*Type*: `str`
+
+Symbol to replace the spinner with. Defaults to `' '`.
+
+##### `text`
+*Type*: `str`
+
+Text to be persisted. Defaults to instance text.
+
+
+
+#### `spinner.text`
+Change the text of spinner.
+
+#### `spinner.color`
+Change the color of spinner
+
+#### `spinner.spinner`
+Change the spinner itself.
+
+#### `spinner.enabled`
+Enable or disable the spinner.
+
+## How to contribute?
+
+Please see [Contributing guidelines](https://github.com/manrajgrover/halo/blob/master/.github/CONTRIBUTING.md) for more information.
+
+## Like it?
+
+🌟 this repo to show support. Let me know you liked it on [Twitter](https://twitter.com/manrajsgrover).
+Also, share the [project](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.amrom.workers.dev%2Fmanrajgrover%2Fhalo&via=manrajsgrover&text=Checkout%20%23halo%20-%20a%20beautiful%20%23terminal%20%23spinners%20library%20for%20%23python&hashtags=github%2C%20pypi).
+
+## Related
+
+* [py-spinners](https://github.com/manrajgrover/py-spinners) - Spinners in Python
+* [py-log-symbols](https://github.com/manrajgrover/py-log-symbols) - Log Symbols in Python
+* [ora](https://github.com/sindresorhus/ora) - Elegant terminal spinners in JavaScript (inspiration behind this project)
+
+## License
+[MIT](https://github.com/manrajgrover/halo/blob/master/LICENSE) © Manraj Singh
+
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/RECORD b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/RECORD
new file mode 100644
index 00000000..022d65b4
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/RECORD
@@ -0,0 +1,17 @@
+halo-0.0.31.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+halo-0.0.31.dist-info/LICENSE,sha256=R2t7cIDZ6-VroHO5M_FWET8adqmdI-PGv0AnhyvCo4A,1069
+halo-0.0.31.dist-info/METADATA,sha256=azLsRMInovCX1dGE0E2InFMbyQJUqFm2YcqfewD93lM,7088
+halo-0.0.31.dist-info/RECORD,,
+halo-0.0.31.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+halo-0.0.31.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+halo-0.0.31.dist-info/top_level.txt,sha256=LEvREToTCAduUBWpz2ktV7qe8OlPO63JGUYbX4wQVXI,5
+halo/__init__.py,sha256=_a2qkLkcB4Lw42TZau7lmdvBZeGGum-Spoz4GmQ9qm4,237
+halo/__pycache__/__init__.cpython-310.pyc,,
+halo/__pycache__/_utils.cpython-310.pyc,,
+halo/__pycache__/cursor.cpython-310.pyc,,
+halo/__pycache__/halo.cpython-310.pyc,,
+halo/__pycache__/halo_notebook.cpython-310.pyc,,
+halo/_utils.py,sha256=t7wEz0Gr17uyO-NDT3qx2foyjYKG8_G_8i9xGTFvCx0,2995
+halo/cursor.py,sha256=nmUO1ouWBOUBgpGBzpQ5pqcRe802Z5GJ-f8ghv0r0ns,1321
+halo/halo.py,sha256=okUK8a2RS1VTQlAvGBQhx6NqpRouVzFaHuQvyNBpyic,16610
+halo/halo_notebook.py,sha256=c-nrJZVHcJg868v9wXQmg44SAO8Uu9hxuc92K1fiqL8,3183
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/REQUESTED b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/WHEEL b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/top_level.txt b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/top_level.txt
new file mode 100644
index 00000000..1185b465
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo-0.0.31.dist-info/top_level.txt
@@ -0,0 +1 @@
+halo
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__init__.py b/provision-contest/ansible/files/admin-python/site-packages/halo/__init__.py
new file mode 100644
index 00000000..e9d67d92
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+__author__ = 'Manraj Singh'
+__email__ = 'manrajsinghgrover@gmail.com'
+
+import logging
+
+from .halo import Halo
+from .halo_notebook import HaloNotebook
+
+logging.getLogger(__name__).addHandler(logging.NullHandler())
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 00000000..f41907ef
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 00000000..e5778744
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/__init__.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-310.pyc
new file mode 100644
index 00000000..34c47c90
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-38.pyc
new file mode 100644
index 00000000..164badc8
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/_utils.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-310.pyc
new file mode 100644
index 00000000..44a03439
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-38.pyc
new file mode 100644
index 00000000..cbc9ef16
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/cursor.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-310.pyc
new file mode 100644
index 00000000..e41b54d0
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-38.pyc
new file mode 100644
index 00000000..0fde7e24
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-310.pyc
new file mode 100644
index 00000000..2c65e730
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-38.pyc
new file mode 100644
index 00000000..5b16da39
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/halo/__pycache__/halo_notebook.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/_utils.py b/provision-contest/ansible/files/admin-python/site-packages/halo/_utils.py
new file mode 100644
index 00000000..999a95b8
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo/_utils.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+"""Utilities for Halo library.
+"""
+import codecs
+import platform
+import six
+try:
+ from shutil import get_terminal_size
+except ImportError:
+ from backports.shutil_get_terminal_size import get_terminal_size
+
+from colorama import init
+from termcolor import colored
+
+init(autoreset=True)
+
+
+def is_supported():
+ """Check whether operating system supports main symbols or not.
+
+ Returns
+ -------
+ boolean
+ Whether operating system supports main symbols or not
+ """
+
+ os_arch = platform.system()
+
+ if os_arch != 'Windows':
+ return True
+
+ return False
+
+
+def get_environment():
+ """Get the environment in which halo is running
+
+ Returns
+ -------
+ str
+ Environment name
+ """
+ try:
+ from IPython import get_ipython
+ except ImportError:
+ return 'terminal'
+
+ try:
+ shell = get_ipython().__class__.__name__
+
+ if shell == 'ZMQInteractiveShell': # Jupyter notebook or qtconsole
+ return 'jupyter'
+ elif shell == 'TerminalInteractiveShell': # Terminal running IPython
+ return 'ipython'
+ else:
+ return 'terminal' # Other type (?)
+
+ except NameError:
+ return 'terminal'
+
+
+def colored_frame(frame, color):
+ """Color the frame with given color and returns.
+
+ Parameters
+ ----------
+ frame : str
+ Frame to be colored
+ color : str
+ Color to be applied
+
+ Returns
+ -------
+ str
+ Colored frame
+ """
+ return colored(frame, color, attrs=['bold'])
+
+
+def is_text_type(text):
+ """Check if given parameter is a string or not
+
+ Parameters
+ ----------
+ text : *
+ Parameter to be checked for text type
+
+ Returns
+ -------
+ bool
+ Whether parameter is a string or not
+ """
+ if isinstance(text, six.text_type) or isinstance(text, six.string_types):
+ return True
+
+ return False
+
+
+def decode_utf_8_text(text):
+ """Decode the text from utf-8 format
+
+ Parameters
+ ----------
+ text : str
+ String to be decoded
+
+ Returns
+ -------
+ str
+ Decoded string
+ """
+ try:
+ return codecs.decode(text, 'utf-8')
+ except (TypeError, ValueError):
+ return text
+
+
+def encode_utf_8_text(text):
+ """Encodes the text to utf-8 format
+
+ Parameters
+ ----------
+ text : str
+ String to be encoded
+
+ Returns
+ -------
+ str
+ Encoded string
+ """
+ try:
+ return codecs.encode(text, 'utf-8', 'ignore')
+ except (TypeError, ValueError):
+ return text
+
+
+def get_terminal_columns():
+ """Determine the amount of available columns in the terminal
+
+ Returns
+ -------
+ int
+ Terminal width
+ """
+ terminal_size = get_terminal_size()
+
+ # If column size is 0 either we are not connected
+ # to a terminal or something else went wrong. Fallback to 80.
+ if terminal_size.columns == 0:
+ return 80
+ else:
+ return terminal_size.columns
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/cursor.py b/provision-contest/ansible/files/admin-python/site-packages/halo/cursor.py
new file mode 100644
index 00000000..b0e54c9f
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo/cursor.py
@@ -0,0 +1,48 @@
+"""
+Source: https://stackoverflow.com/a/10455937/2692667
+"""
+
+import sys
+import os
+
+if os.name == "nt":
+ import ctypes
+
+ class _CursorInfo(ctypes.Structure):
+ _fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
+
+
+def hide(stream=sys.stdout):
+ """Hide cursor.
+ Parameters
+ ----------
+ stream: sys.stdout, Optional
+ Defines stream to write output to.
+ """
+ if os.name == "nt":
+ ci = _CursorInfo()
+ handle = ctypes.windll.kernel32.GetStdHandle(-11)
+ ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
+ ci.visible = False
+ ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
+ elif os.name == "posix":
+ stream.write("\033[?25l")
+ stream.flush()
+
+
+def show(stream=sys.stdout):
+ """Show cursor.
+ Parameters
+ ----------
+ stream: sys.stdout, Optional
+ Defines stream to write output to.
+ """
+ if os.name == "nt":
+ ci = _CursorInfo()
+ handle = ctypes.windll.kernel32.GetStdHandle(-11)
+ ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
+ ci.visible = True
+ ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
+ elif os.name == "posix":
+ stream.write("\033[?25h")
+ stream.flush()
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/halo.py b/provision-contest/ansible/files/admin-python/site-packages/halo/halo.py
new file mode 100644
index 00000000..9e10b66b
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo/halo.py
@@ -0,0 +1,609 @@
+# -*- coding: utf-8 -*-
+# pylint: disable=unsubscriptable-object
+"""Beautiful terminal spinners in Python.
+"""
+from __future__ import absolute_import, unicode_literals
+
+import atexit
+import functools
+import sys
+import threading
+import time
+
+import halo.cursor as cursor
+
+from log_symbols.symbols import LogSymbols
+from spinners.spinners import Spinners
+
+from halo._utils import (
+ colored_frame,
+ decode_utf_8_text,
+ get_environment,
+ get_terminal_columns,
+ is_supported,
+ is_text_type,
+ encode_utf_8_text,
+)
+
+
+class Halo(object):
+ """Halo library.
+ Attributes
+ ----------
+ CLEAR_LINE : str
+ Code to clear the line
+ """
+
+ CLEAR_LINE = "\033[K"
+ SPINNER_PLACEMENTS = (
+ "left",
+ "right",
+ )
+
+ def __init__(
+ self,
+ text="",
+ color="cyan",
+ text_color=None,
+ spinner=None,
+ animation=None,
+ placement="left",
+ interval=-1,
+ enabled=True,
+ stream=sys.stdout,
+ ):
+ """Constructs the Halo object.
+ Parameters
+ ----------
+ text : str, optional
+ Text to display.
+ text_color : str, optional
+ Color of the text.
+ color : str, optional
+ Color of the text to display.
+ spinner : str|dict, optional
+ String or dictionary representing spinner. String can be one of 60+ spinners
+ supported.
+ animation: str, optional
+ Animation to apply if text is too large. Can be one of `bounce`, `marquee`.
+ Defaults to ellipses.
+ placement: str, optional
+ Side of the text to place the spinner on. Can be `left` or `right`.
+ Defaults to `left`.
+ interval : integer, optional
+ Interval between each frame of the spinner in milliseconds.
+ enabled : boolean, optional
+ Spinner enabled or not.
+ stream : io, optional
+ Output.
+ """
+ self._color = color
+ self._animation = animation
+
+ self.spinner = spinner
+ self.text = text
+ self._text_color = text_color
+
+ self._interval = (
+ int(interval) if int(interval) > 0 else self._spinner["interval"]
+ )
+ self._stream = stream
+
+ self.placement = placement
+ self._frame_index = 0
+ self._text_index = 0
+ self._spinner_thread = None
+ self._stop_spinner = None
+ self._spinner_id = None
+ self.enabled = enabled
+
+ environment = get_environment()
+
+ def clean_up():
+ """Handle cell execution"""
+ self.stop()
+
+ if environment in ("ipython", "jupyter"):
+ from IPython import get_ipython
+
+ ip = get_ipython()
+ ip.events.register("post_run_cell", clean_up)
+ else: # default terminal
+ atexit.register(clean_up)
+
+ def __enter__(self):
+ """Starts the spinner on a separate thread. For use in context managers.
+ Returns
+ -------
+ self
+ """
+ return self.start()
+
+ def __exit__(self, type, value, traceback):
+ """Stops the spinner. For use in context managers."""
+ self.stop()
+
+ def __call__(self, f):
+ """Allow the Halo object to be used as a regular function decorator."""
+
+ @functools.wraps(f)
+ def wrapped(*args, **kwargs):
+ with self:
+ return f(*args, **kwargs)
+
+ return wrapped
+
+ @property
+ def spinner(self):
+ """Getter for spinner property.
+ Returns
+ -------
+ dict
+ spinner value
+ """
+ return self._spinner
+
+ @spinner.setter
+ def spinner(self, spinner=None):
+ """Setter for spinner property.
+ Parameters
+ ----------
+ spinner : dict, str
+ Defines the spinner value with frame and interval
+ """
+
+ self._spinner = self._get_spinner(spinner)
+ self._frame_index = 0
+ self._text_index = 0
+
+ @property
+ def text(self):
+ """Getter for text property.
+ Returns
+ -------
+ str
+ text value
+ """
+ return self._text["original"]
+
+ @text.setter
+ def text(self, text):
+ """Setter for text property.
+ Parameters
+ ----------
+ text : str
+ Defines the text value for spinner
+ """
+ self._text = self._get_text(text)
+
+ @property
+ def text_color(self):
+ """Getter for text color property.
+ Returns
+ -------
+ str
+ text color value
+ """
+ return self._text_color
+
+ @text_color.setter
+ def text_color(self, text_color):
+ """Setter for text color property.
+ Parameters
+ ----------
+ text_color : str
+ Defines the text color value for spinner
+ """
+ self._text_color = text_color
+
+ @property
+ def color(self):
+ """Getter for color property.
+ Returns
+ -------
+ str
+ color value
+ """
+ return self._color
+
+ @color.setter
+ def color(self, color):
+ """Setter for color property.
+ Parameters
+ ----------
+ color : str
+ Defines the color value for spinner
+ """
+ self._color = color
+
+ @property
+ def placement(self):
+ """Getter for placement property.
+ Returns
+ -------
+ str
+ spinner placement
+ """
+ return self._placement
+
+ @placement.setter
+ def placement(self, placement):
+ """Setter for placement property.
+ Parameters
+ ----------
+ placement: str
+ Defines the placement of the spinner
+ """
+ if placement not in self.SPINNER_PLACEMENTS:
+ raise ValueError(
+ "Unknown spinner placement '{0}', available are {1}".format(
+ placement, self.SPINNER_PLACEMENTS
+ )
+ )
+ self._placement = placement
+
+ @property
+ def spinner_id(self):
+ """Getter for spinner id
+ Returns
+ -------
+ str
+ Spinner id value
+ """
+ return self._spinner_id
+
+ @property
+ def animation(self):
+ """Getter for animation property.
+ Returns
+ -------
+ str
+ Spinner animation
+ """
+ return self._animation
+
+ @animation.setter
+ def animation(self, animation):
+ """Setter for animation property.
+ Parameters
+ ----------
+ animation: str
+ Defines the animation of the spinner
+ """
+ self._animation = animation
+ self._text = self._get_text(self._text["original"])
+
+ def _check_stream(self):
+ """Returns whether the stream is open, and if applicable, writable
+ Returns
+ -------
+ bool
+ Whether the stream is open
+ """
+ if self._stream.closed:
+ return False
+
+ try:
+ # Attribute access kept separate from invocation, to avoid
+ # swallowing AttributeErrors from the call which should bubble up.
+ check_stream_writable = self._stream.writable
+ except AttributeError:
+ pass
+ else:
+ return check_stream_writable()
+
+ return True
+
+ def _write(self, s):
+ """Write to the stream, if writable
+ Parameters
+ ----------
+ s : str
+ Characters to write to the stream
+ """
+ if self._check_stream():
+ self._stream.write(s)
+
+ def _hide_cursor(self):
+ """Disable the user's blinking cursor
+ """
+ if self._check_stream() and self._stream.isatty():
+ cursor.hide(stream=self._stream)
+
+ def _show_cursor(self):
+ """Re-enable the user's blinking cursor
+ """
+ if self._check_stream() and self._stream.isatty():
+ cursor.show(stream=self._stream)
+
+ def _get_spinner(self, spinner):
+ """Extracts spinner value from options and returns value
+ containing spinner frames and interval, defaults to 'dots' spinner.
+ Parameters
+ ----------
+ spinner : dict, str
+ Contains spinner value or type of spinner to be used
+ Returns
+ -------
+ dict
+ Contains frames and interval defining spinner
+ """
+ default_spinner = Spinners["dots"].value
+
+ if spinner and type(spinner) == dict:
+ return spinner
+
+ if is_supported():
+ if all([is_text_type(spinner), spinner in Spinners.__members__]):
+ return Spinners[spinner].value
+ else:
+ return default_spinner
+ else:
+ return Spinners["line"].value
+
+ def _get_text(self, text):
+ """Creates frames based on the selected animation
+ Returns
+ -------
+ self
+ """
+ animation = self._animation
+ stripped_text = text.strip()
+
+ # Check which frame of the animation is the widest
+ max_spinner_length = max([len(i) for i in self._spinner["frames"]])
+
+ # Subtract to the current terminal size the max spinner length
+ # (-1 to leave room for the extra space between spinner and text)
+ terminal_width = get_terminal_columns() - max_spinner_length - 1
+ text_length = len(stripped_text)
+
+ frames = []
+
+ if terminal_width < text_length and animation:
+ if animation == "bounce":
+ """
+ Make the text bounce back and forth
+ """
+ for x in range(0, text_length - terminal_width + 1):
+ frames.append(stripped_text[x : terminal_width + x])
+ frames.extend(list(reversed(frames)))
+ elif "marquee":
+ """
+ Make the text scroll like a marquee
+ """
+ stripped_text = stripped_text + " " + stripped_text[:terminal_width]
+ for x in range(0, text_length + 1):
+ frames.append(stripped_text[x : terminal_width + x])
+ elif terminal_width < text_length and not animation:
+ # Add ellipsis if text is larger than terminal width and no animation was specified
+ frames = [stripped_text[: terminal_width - 6] + " (...)"]
+ else:
+ frames = [stripped_text]
+
+ return {"original": text, "frames": frames}
+
+ def clear(self):
+ """Clears the line and returns cursor to the start.
+ of line
+ Returns
+ -------
+ self
+ """
+ self._write("\r")
+ self._write(self.CLEAR_LINE)
+ return self
+
+ def _render_frame(self):
+ """Renders the frame on the line after clearing it.
+ """
+ if not self.enabled:
+ # in case we're disabled or stream is closed while still rendering,
+ # we render the frame and increment the frame index, so the proper
+ # frame is rendered if we're reenabled or the stream opens again.
+ return
+
+ self.clear()
+ frame = self.frame()
+ output = "\r{}".format(frame)
+ try:
+ self._write(output)
+ except UnicodeEncodeError:
+ self._write(encode_utf_8_text(output))
+
+ def render(self):
+ """Runs the render until thread flag is set.
+ Returns
+ -------
+ self
+ """
+ while not self._stop_spinner.is_set():
+ self._render_frame()
+ time.sleep(0.001 * self._interval)
+
+ return self
+
+ def frame(self):
+ """Builds and returns the frame to be rendered
+ Returns
+ -------
+ self
+ """
+ frames = self._spinner["frames"]
+ frame = frames[self._frame_index]
+
+ if self._color:
+ frame = colored_frame(frame, self._color)
+
+ self._frame_index += 1
+ self._frame_index = self._frame_index % len(frames)
+
+ text_frame = self.text_frame()
+ return "{0} {1}".format(
+ *[
+ (text_frame, frame)
+ if self._placement == "right"
+ else (frame, text_frame)
+ ][0]
+ )
+
+ def text_frame(self):
+ """Builds and returns the text frame to be rendered
+ Returns
+ -------
+ self
+ """
+ if len(self._text["frames"]) == 1:
+ if self._text_color:
+ return colored_frame(self._text["frames"][0], self._text_color)
+
+ # Return first frame (can't return original text because at this point it might be ellipsed)
+ return self._text["frames"][0]
+
+ frames = self._text["frames"]
+ frame = frames[self._text_index]
+
+ self._text_index += 1
+ self._text_index = self._text_index % len(frames)
+
+ if self._text_color:
+ return colored_frame(frame, self._text_color)
+
+ return frame
+
+ def start(self, text=None):
+ """Starts the spinner on a separate thread.
+ Parameters
+ ----------
+ text : None, optional
+ Text to be used alongside spinner
+ Returns
+ -------
+ self
+ """
+ if text is not None:
+ self.text = text
+
+ if self._spinner_id is not None:
+ return self
+
+ if not (self.enabled and self._check_stream()):
+ return self
+
+ self._hide_cursor()
+
+ self._stop_spinner = threading.Event()
+ self._spinner_thread = threading.Thread(target=self.render)
+ self._spinner_thread.setDaemon(True)
+ self._render_frame()
+ self._spinner_id = self._spinner_thread.name
+ self._spinner_thread.start()
+
+ return self
+
+ def stop(self):
+ """Stops the spinner and clears the line.
+ Returns
+ -------
+ self
+ """
+ if self._spinner_thread and self._spinner_thread.is_alive():
+ self._stop_spinner.set()
+ self._spinner_thread.join()
+
+ if self.enabled:
+ self.clear()
+
+ self._frame_index = 0
+ self._spinner_id = None
+ self._show_cursor()
+ return self
+
+ def succeed(self, text=None):
+ """Shows and persists success symbol and text and exits.
+ Parameters
+ ----------
+ text : None, optional
+ Text to be shown alongside success symbol.
+ Returns
+ -------
+ self
+ """
+ return self.stop_and_persist(symbol=LogSymbols.SUCCESS.value, text=text)
+
+ def fail(self, text=None):
+ """Shows and persists fail symbol and text and exits.
+ Parameters
+ ----------
+ text : None, optional
+ Text to be shown alongside fail symbol.
+ Returns
+ -------
+ self
+ """
+ return self.stop_and_persist(symbol=LogSymbols.ERROR.value, text=text)
+
+ def warn(self, text=None):
+ """Shows and persists warn symbol and text and exits.
+ Parameters
+ ----------
+ text : None, optional
+ Text to be shown alongside warn symbol.
+ Returns
+ -------
+ self
+ """
+ return self.stop_and_persist(symbol=LogSymbols.WARNING.value, text=text)
+
+ def info(self, text=None):
+ """Shows and persists info symbol and text and exits.
+ Parameters
+ ----------
+ text : None, optional
+ Text to be shown alongside info symbol.
+ Returns
+ -------
+ self
+ """
+ return self.stop_and_persist(symbol=LogSymbols.INFO.value, text=text)
+
+ def stop_and_persist(self, symbol=" ", text=None):
+ """Stops the spinner and persists the final frame to be shown.
+ Parameters
+ ----------
+ symbol : str, optional
+ Symbol to be shown in final frame
+ text: str, optional
+ Text to be shown in final frame
+
+ Returns
+ -------
+ self
+ """
+ if not self.enabled:
+ return self
+
+ symbol = decode_utf_8_text(symbol)
+
+ if text is not None:
+ text = decode_utf_8_text(text)
+ else:
+ text = self._text["original"]
+
+ text = text.strip()
+
+ if self._text_color:
+ text = colored_frame(text, self._text_color)
+
+ self.stop()
+
+ output = "{0} {1}\n".format(
+ *[(text, symbol) if self._placement == "right" else (symbol, text)][0]
+ )
+
+ try:
+ self._write(output)
+ except UnicodeEncodeError:
+ self._write(encode_utf_8_text(output))
+
+ return self
diff --git a/provision-contest/ansible/files/admin-python/site-packages/halo/halo_notebook.py b/provision-contest/ansible/files/admin-python/site-packages/halo/halo_notebook.py
new file mode 100644
index 00000000..753fd7ac
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/halo/halo_notebook.py
@@ -0,0 +1,122 @@
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import threading
+
+import halo.cursor as cursor
+
+from halo import Halo
+from halo._utils import colored_frame, decode_utf_8_text
+
+
+class HaloNotebook(Halo):
+ def __init__(
+ self,
+ text="",
+ color="cyan",
+ text_color=None,
+ spinner=None,
+ placement="left",
+ animation=None,
+ interval=-1,
+ enabled=True,
+ stream=sys.stdout,
+ ):
+ super(HaloNotebook, self).__init__(
+ text=text,
+ color=color,
+ text_color=text_color,
+ spinner=spinner,
+ placement=placement,
+ animation=animation,
+ interval=interval,
+ enabled=enabled,
+ stream=stream,
+ )
+ self.output = self._make_output_widget()
+
+ def _make_output_widget(self):
+ from ipywidgets.widgets import Output
+
+ return Output()
+
+ # TODO: using property and setter
+ def _output(self, text=""):
+ return ({"name": "stdout", "output_type": "stream", "text": text},)
+
+ def clear(self):
+ if not self.enabled:
+ return self
+
+ with self.output:
+ self.output.outputs += self._output("\r")
+ self.output.outputs += self._output(self.CLEAR_LINE)
+
+ self.output.outputs = self._output()
+ return self
+
+ def _render_frame(self):
+ frame = self.frame()
+ output = "\r{}".format(frame)
+ with self.output:
+ self.output.outputs += self._output(output)
+
+ def start(self, text=None):
+ if text is not None:
+ self.text = text
+
+ if not self.enabled or self._spinner_id is not None:
+ return self
+
+ if self._stream.isatty():
+ cursor.hide()
+
+ self.output = self._make_output_widget()
+ from IPython.display import display
+
+ display(self.output)
+ self._stop_spinner = threading.Event()
+ self._spinner_thread = threading.Thread(target=self.render)
+ self._spinner_thread.setDaemon(True)
+ self._render_frame()
+ self._spinner_id = self._spinner_thread.name
+ self._spinner_thread.start()
+
+ return self
+
+ def stop_and_persist(self, symbol=" ", text=None):
+ """Stops the spinner and persists the final frame to be shown.
+ Parameters
+ ----------
+ symbol : str, optional
+ Symbol to be shown in final frame
+ text: str, optional
+ Text to be shown in final frame
+
+ Returns
+ -------
+ self
+ """
+ if not self.enabled:
+ return self
+
+ symbol = decode_utf_8_text(symbol)
+
+ if text is not None:
+ text = decode_utf_8_text(text)
+ else:
+ text = self._text["original"]
+
+ text = text.strip()
+
+ if self._text_color:
+ text = colored_frame(text, self._text_color)
+
+ self.stop()
+
+ output = "\r{} {}\n".format(
+ *[(text, symbol) if self._placement == "right" else (symbol, text)][0]
+ )
+
+ with self.output:
+ self.output.outputs = self._output(output)
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/INSTALLER b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/LICENSE b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/LICENSE
new file mode 100644
index 00000000..9b114e03
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Manraj Singh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/METADATA b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/METADATA
new file mode 100644
index 00000000..e67b1ad1
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/METADATA
@@ -0,0 +1,16 @@
+Metadata-Version: 2.1
+Name: log-symbols
+Version: 0.0.14
+Summary: Colored symbols for various log levels for Python
+Home-page: https://github.com/manrajgrover/py-log-symbols
+Author: Manraj Singh
+Author-email: manrajsinghgrover@gmail.com
+License: MIT
+Keywords: log symbols,symbols,log
+Platform: UNKNOWN
+Requires-Dist: colorama (>=0.3.9)
+Requires-Dist: enum34 (==1.1.6) ; python_version < "3.4"
+
+Colored symbols for various log levels for Python. Find the documentation here: https://github.com/manrajgrover/py-log-symbols.
+
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/RECORD b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/RECORD
new file mode 100644
index 00000000..d5bba0a3
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/RECORD
@@ -0,0 +1,10 @@
+log_symbols-0.0.14.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+log_symbols-0.0.14.dist-info/LICENSE,sha256=R2t7cIDZ6-VroHO5M_FWET8adqmdI-PGv0AnhyvCo4A,1069
+log_symbols-0.0.14.dist-info/METADATA,sha256=cHv9QxJp7AaSrrnDMYjD1Q0ffgKVtD1n7YsF9qLHyC8,523
+log_symbols-0.0.14.dist-info/RECORD,,
+log_symbols-0.0.14.dist-info/WHEEL,sha256=S8S5VL-stOTSZDYxHyf0KP7eds0J72qrK0Evu3TfyAY,92
+log_symbols-0.0.14.dist-info/top_level.txt,sha256=Mq8LH58W68d6O7zejMld8iZaLebMYm6ZK4RS-A52peI,12
+log_symbols/__init__.py,sha256=ULFhsDEyAEzfJYo3f-0rnMUQxO0FzQ1povrEPyjMGjI,127
+log_symbols/__pycache__/__init__.cpython-310.pyc,,
+log_symbols/__pycache__/symbols.cpython-310.pyc,,
+log_symbols/symbols.py,sha256=1cSjjIzw4_nGGUQB5Mj-TwZFYmx2m6rXjiV-CVNfGts,1276
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/WHEEL b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/WHEEL
new file mode 100644
index 00000000..c57a5970
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/top_level.txt b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/top_level.txt
new file mode 100644
index 00000000..42af2dd2
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols-0.0.14.dist-info/top_level.txt
@@ -0,0 +1 @@
+log_symbols
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__init__.py b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__init__.py
new file mode 100644
index 00000000..9e63b021
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+__author__ = 'Manraj Singh'
+__email__ = 'manrajsinghgrover@gmail.com'
+
+from .symbols import LogSymbols
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 00000000..075e9c1b
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 00000000..c00e5265
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/__init__.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-310.pyc
new file mode 100644
index 00000000..e3d1a7be
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-38.pyc
new file mode 100644
index 00000000..9e25222b
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/__pycache__/symbols.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/log_symbols/symbols.py b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/symbols.py
new file mode 100644
index 00000000..e963d31d
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/log_symbols/symbols.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+"""Provide log symbols for various log levels."""
+import platform
+
+from enum import Enum
+from colorama import init, deinit, Fore
+
+init(autoreset=True)
+
+_MAIN = {
+ 'info': 'ℹ',
+ 'success': '✔',
+ 'warning': '⚠',
+ 'error': '✖'
+}
+
+_FALLBACKS = {
+ 'info': '¡',
+ 'success': 'v',
+ 'warning': '!!',
+ 'error': '×'
+}
+
+
+def is_supported():
+ """Check whether operating system supports main symbols or not.
+
+ Returns
+ -------
+ boolean
+ Whether operating system supports main symbols or not
+ """
+
+ os_arch = platform.system()
+
+ if os_arch != 'Windows':
+ return True
+
+ return False
+
+
+_SYMBOLS = _MAIN if is_supported() else _FALLBACKS
+
+
+class LogSymbols(Enum): # pylint: disable=too-few-public-methods
+ """LogSymbol enum class.
+
+ Attributes
+ ----------
+ ERROR : str
+ Colored error symbol
+ INFO : str
+ Colored info symbol
+ SUCCESS : str
+ Colored success symbol
+ WARNING : str
+ Colored warning symbol
+ """
+
+ INFO = Fore.BLUE + _SYMBOLS['info'] + Fore.RESET
+ SUCCESS = Fore.GREEN + _SYMBOLS['success'] + Fore.RESET
+ WARNING = Fore.YELLOW + _SYMBOLS['warning'] + Fore.RESET
+ ERROR = Fore.RED + _SYMBOLS['error'] + Fore.RESET
+
+deinit()
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/INSTALLER b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/LICENSE b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/LICENSE
new file mode 100644
index 00000000..9b114e03
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Manraj Singh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/METADATA b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/METADATA
new file mode 100644
index 00000000..f8618054
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/METADATA
@@ -0,0 +1,15 @@
+Metadata-Version: 2.1
+Name: spinners
+Version: 0.0.24
+Summary: Spinners for terminals
+Home-page: https://github.com/manrajgrover/py-spinners
+Author: Manraj Singh
+Author-email: manrajsinghgrover@gmail.com
+License: MIT
+Keywords: cli,spinner,spinners,terminal,term,console,ascii,unicode,loading,indicator,progress,busy,wait,idle,json
+Platform: UNKNOWN
+Requires-Dist: enum34 (==1.1.6) ; python_version < "3.4"
+
+More than 60 spinners for terminal, this is python port of amazing node library cli-spinners. Find the documentation here: https://github.com/manrajgrover/py-spinners.
+
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/RECORD b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/RECORD
new file mode 100644
index 00000000..51d0fad7
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/RECORD
@@ -0,0 +1,10 @@
+spinners-0.0.24.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+spinners-0.0.24.dist-info/LICENSE,sha256=R2t7cIDZ6-VroHO5M_FWET8adqmdI-PGv0AnhyvCo4A,1069
+spinners-0.0.24.dist-info/METADATA,sha256=cJ34-hmKWJZL898eLFz0BhL6WBykUsFtzOMKebuTWMA,576
+spinners-0.0.24.dist-info/RECORD,,
+spinners-0.0.24.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92
+spinners-0.0.24.dist-info/top_level.txt,sha256=P6azeuTfm-k56cPZr6jTxI0gHW-YM6H9nn3t-s7760o,9
+spinners/__init__.py,sha256=33bmNOuNt5tMmHsNge8cANVYXMAtNm_GAA2zDvvksuY,102
+spinners/__pycache__/__init__.cpython-310.pyc,,
+spinners/__pycache__/spinners.cpython-310.pyc,,
+spinners/spinners.py,sha256=UV7oVBbWizTWek87ExGSFhoP8fi5hIDYIYLAGhIL5J8,18813
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/WHEEL b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/WHEEL
new file mode 100644
index 00000000..3b5c4038
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/top_level.txt b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/top_level.txt
new file mode 100644
index 00000000..e67b0824
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners-0.0.24.dist-info/top_level.txt
@@ -0,0 +1 @@
+spinners
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/__init__.py b/provision-contest/ansible/files/admin-python/site-packages/spinners/__init__.py
new file mode 100644
index 00000000..969320e1
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners/__init__.py
@@ -0,0 +1,4 @@
+__author__ = 'Manraj Singh'
+__email__ = 'manrajsinghgrover@gmail.com'
+
+from .spinners import Spinners
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 00000000..cb45193b
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 00000000..740d217b
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/__init__.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-310.pyc
new file mode 100644
index 00000000..5e90368f
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-38.pyc
new file mode 100644
index 00000000..cea54fbf
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/spinners/__pycache__/spinners.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/spinners/spinners.py b/provision-contest/ansible/files/admin-python/site-packages/spinners/spinners.py
new file mode 100644
index 00000000..7eee4f1a
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/spinners/spinners.py
@@ -0,0 +1,943 @@
+# -*- coding: utf-8 -*-
+"""
+Python wrapper for beautiful terminal spinner library.
+
+Spinners are from:
+* cli-spinners:
+ MIT License
+
+ Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ the Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+"""
+from __future__ import unicode_literals
+
+from enum import Enum
+
+Spinners = Enum('Spinners', {
+ "dots": {
+ "interval": 80,
+ "frames": [
+ "⠋",
+ "⠙",
+ "⠹",
+ "⠸",
+ "⠼",
+ "⠴",
+ "⠦",
+ "⠧",
+ "⠇",
+ "⠏"
+ ]
+ },
+ "dots2": {
+ "interval": 80,
+ "frames": [
+ "⣾",
+ "⣽",
+ "⣻",
+ "⢿",
+ "⡿",
+ "⣟",
+ "⣯",
+ "⣷"
+ ]
+ },
+ "dots3": {
+ "interval": 80,
+ "frames": [
+ "⠋",
+ "⠙",
+ "⠚",
+ "⠞",
+ "⠖",
+ "⠦",
+ "⠴",
+ "⠲",
+ "⠳",
+ "⠓"
+ ]
+ },
+ "dots4": {
+ "interval": 80,
+ "frames": [
+ "⠄",
+ "⠆",
+ "⠇",
+ "⠋",
+ "⠙",
+ "⠸",
+ "⠰",
+ "⠠",
+ "⠰",
+ "⠸",
+ "⠙",
+ "⠋",
+ "⠇",
+ "⠆"
+ ]
+ },
+ "dots5": {
+ "interval": 80,
+ "frames": [
+ "⠋",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋"
+ ]
+ },
+ "dots6": {
+ "interval": 80,
+ "frames": [
+ "⠁",
+ "⠉",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠤",
+ "⠄",
+ "⠄",
+ "⠤",
+ "⠴",
+ "⠲",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠚",
+ "⠙",
+ "⠉",
+ "⠁"
+ ]
+ },
+ "dots7": {
+ "interval": 80,
+ "frames": [
+ "⠈",
+ "⠉",
+ "⠋",
+ "⠓",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠖",
+ "⠦",
+ "⠤",
+ "⠠",
+ "⠠",
+ "⠤",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋",
+ "⠉",
+ "⠈"
+ ]
+ },
+ "dots8": {
+ "interval": 80,
+ "frames": [
+ "⠁",
+ "⠁",
+ "⠉",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠤",
+ "⠄",
+ "⠄",
+ "⠤",
+ "⠠",
+ "⠠",
+ "⠤",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋",
+ "⠉",
+ "⠈",
+ "⠈"
+ ]
+ },
+ "dots9": {
+ "interval": 80,
+ "frames": [
+ "⢹",
+ "⢺",
+ "⢼",
+ "⣸",
+ "⣇",
+ "⡧",
+ "⡗",
+ "⡏"
+ ]
+ },
+ "dots10": {
+ "interval": 80,
+ "frames": [
+ "⢄",
+ "⢂",
+ "⢁",
+ "⡁",
+ "⡈",
+ "⡐",
+ "⡠"
+ ]
+ },
+ "dots11": {
+ "interval": 100,
+ "frames": [
+ "⠁",
+ "⠂",
+ "⠄",
+ "⡀",
+ "⢀",
+ "⠠",
+ "⠐",
+ "⠈"
+ ]
+ },
+ "dots12": {
+ "interval": 80,
+ "frames": [
+ "⢀⠀",
+ "⡀⠀",
+ "⠄⠀",
+ "⢂⠀",
+ "⡂⠀",
+ "⠅⠀",
+ "⢃⠀",
+ "⡃⠀",
+ "⠍⠀",
+ "⢋⠀",
+ "⡋⠀",
+ "⠍⠁",
+ "⢋⠁",
+ "⡋⠁",
+ "⠍⠉",
+ "⠋⠉",
+ "⠋⠉",
+ "⠉⠙",
+ "⠉⠙",
+ "⠉⠩",
+ "⠈⢙",
+ "⠈⡙",
+ "⢈⠩",
+ "⡀⢙",
+ "⠄⡙",
+ "⢂⠩",
+ "⡂⢘",
+ "⠅⡘",
+ "⢃⠨",
+ "⡃⢐",
+ "⠍⡐",
+ "⢋⠠",
+ "⡋⢀",
+ "⠍⡁",
+ "⢋⠁",
+ "⡋⠁",
+ "⠍⠉",
+ "⠋⠉",
+ "⠋⠉",
+ "⠉⠙",
+ "⠉⠙",
+ "⠉⠩",
+ "⠈⢙",
+ "⠈⡙",
+ "⠈⠩",
+ "⠀⢙",
+ "⠀⡙",
+ "⠀⠩",
+ "⠀⢘",
+ "⠀⡘",
+ "⠀⠨",
+ "⠀⢐",
+ "⠀⡐",
+ "⠀⠠",
+ "⠀⢀",
+ "⠀⡀"
+ ]
+ },
+ "line": {
+ "interval": 130,
+ "frames": [
+ "-",
+ "\\",
+ "|",
+ "/"
+ ]
+ },
+ "line2": {
+ "interval": 100,
+ "frames": [
+ "⠂",
+ "-",
+ "–",
+ "—",
+ "–",
+ "-"
+ ]
+ },
+ "pipe": {
+ "interval": 100,
+ "frames": [
+ "┤",
+ "┘",
+ "┴",
+ "└",
+ "├",
+ "┌",
+ "┬",
+ "┐"
+ ]
+ },
+ "simpleDots": {
+ "interval": 400,
+ "frames": [
+ ". ",
+ ".. ",
+ "...",
+ " "
+ ]
+ },
+ "simpleDotsScrolling": {
+ "interval": 200,
+ "frames": [
+ ". ",
+ ".. ",
+ "...",
+ " ..",
+ " .",
+ " "
+ ]
+ },
+ "star": {
+ "interval": 70,
+ "frames": [
+ "✶",
+ "✸",
+ "✹",
+ "✺",
+ "✹",
+ "✷"
+ ]
+ },
+ "star2": {
+ "interval": 80,
+ "frames": [
+ "+",
+ "x",
+ "*"
+ ]
+ },
+ "flip": {
+ "interval": 70,
+ "frames": [
+ "_",
+ "_",
+ "_",
+ "-",
+ "`",
+ "`",
+ "'",
+ "´",
+ "-",
+ "_",
+ "_",
+ "_"
+ ]
+ },
+ "hamburger": {
+ "interval": 100,
+ "frames": [
+ "☱",
+ "☲",
+ "☴"
+ ]
+ },
+ "growVertical": {
+ "interval": 120,
+ "frames": [
+ "▁",
+ "▃",
+ "▄",
+ "▅",
+ "▆",
+ "▇",
+ "▆",
+ "▅",
+ "▄",
+ "▃"
+ ]
+ },
+ "growHorizontal": {
+ "interval": 120,
+ "frames": [
+ "▏",
+ "▎",
+ "▍",
+ "▌",
+ "▋",
+ "▊",
+ "▉",
+ "▊",
+ "▋",
+ "▌",
+ "▍",
+ "▎"
+ ]
+ },
+ "balloon": {
+ "interval": 140,
+ "frames": [
+ " ",
+ ".",
+ "o",
+ "O",
+ "@",
+ "*",
+ " "
+ ]
+ },
+ "balloon2": {
+ "interval": 120,
+ "frames": [
+ ".",
+ "o",
+ "O",
+ "°",
+ "O",
+ "o",
+ "."
+ ]
+ },
+ "noise": {
+ "interval": 100,
+ "frames": [
+ "▓",
+ "▒",
+ "░"
+ ]
+ },
+ "bounce": {
+ "interval": 120,
+ "frames": [
+ "⠁",
+ "⠂",
+ "⠄",
+ "⠂"
+ ]
+ },
+ "boxBounce": {
+ "interval": 120,
+ "frames": [
+ "▖",
+ "▘",
+ "▝",
+ "▗"
+ ]
+ },
+ "boxBounce2": {
+ "interval": 100,
+ "frames": [
+ "▌",
+ "▀",
+ "▐",
+ "▄"
+ ]
+ },
+ "triangle": {
+ "interval": 50,
+ "frames": [
+ "◢",
+ "◣",
+ "◤",
+ "◥"
+ ]
+ },
+ "arc": {
+ "interval": 100,
+ "frames": [
+ "◜",
+ "◠",
+ "◝",
+ "◞",
+ "◡",
+ "◟"
+ ]
+ },
+ "circle": {
+ "interval": 120,
+ "frames": [
+ "◡",
+ "⊙",
+ "◠"
+ ]
+ },
+ "squareCorners": {
+ "interval": 180,
+ "frames": [
+ "◰",
+ "◳",
+ "◲",
+ "◱"
+ ]
+ },
+ "circleQuarters": {
+ "interval": 120,
+ "frames": [
+ "◴",
+ "◷",
+ "◶",
+ "◵"
+ ]
+ },
+ "circleHalves": {
+ "interval": 50,
+ "frames": [
+ "◐",
+ "◓",
+ "◑",
+ "◒"
+ ]
+ },
+ "squish": {
+ "interval": 100,
+ "frames": [
+ "╫",
+ "╪"
+ ]
+ },
+ "toggle": {
+ "interval": 250,
+ "frames": [
+ "⊶",
+ "⊷"
+ ]
+ },
+ "toggle2": {
+ "interval": 80,
+ "frames": [
+ "▫",
+ "▪"
+ ]
+ },
+ "toggle3": {
+ "interval": 120,
+ "frames": [
+ "□",
+ "■"
+ ]
+ },
+ "toggle4": {
+ "interval": 100,
+ "frames": [
+ "■",
+ "□",
+ "▪",
+ "▫"
+ ]
+ },
+ "toggle5": {
+ "interval": 100,
+ "frames": [
+ "▮",
+ "▯"
+ ]
+ },
+ "toggle6": {
+ "interval": 300,
+ "frames": [
+ "ဝ",
+ "၀"
+ ]
+ },
+ "toggle7": {
+ "interval": 80,
+ "frames": [
+ "⦾",
+ "⦿"
+ ]
+ },
+ "toggle8": {
+ "interval": 100,
+ "frames": [
+ "◍",
+ "◌"
+ ]
+ },
+ "toggle9": {
+ "interval": 100,
+ "frames": [
+ "◉",
+ "◎"
+ ]
+ },
+ "toggle10": {
+ "interval": 100,
+ "frames": [
+ "㊂",
+ "㊀",
+ "㊁"
+ ]
+ },
+ "toggle11": {
+ "interval": 50,
+ "frames": [
+ "⧇",
+ "⧆"
+ ]
+ },
+ "toggle12": {
+ "interval": 120,
+ "frames": [
+ "☗",
+ "☖"
+ ]
+ },
+ "toggle13": {
+ "interval": 80,
+ "frames": [
+ "=",
+ "*",
+ "-"
+ ]
+ },
+ "arrow": {
+ "interval": 100,
+ "frames": [
+ "←",
+ "↖",
+ "↑",
+ "↗",
+ "→",
+ "↘",
+ "↓",
+ "↙"
+ ]
+ },
+ "arrow2": {
+ "interval": 80,
+ "frames": [
+ "⬆️ ",
+ "↗️ ",
+ "➡️ ",
+ "↘️ ",
+ "⬇️ ",
+ "↙️ ",
+ "⬅️ ",
+ "↖️ "
+ ]
+ },
+ "arrow3": {
+ "interval": 120,
+ "frames": [
+ "▹▹▹▹▹",
+ "▸▹▹▹▹",
+ "▹▸▹▹▹",
+ "▹▹▸▹▹",
+ "▹▹▹▸▹",
+ "▹▹▹▹▸"
+ ]
+ },
+ "bouncingBar": {
+ "interval": 80,
+ "frames": [
+ "[ ]",
+ "[= ]",
+ "[== ]",
+ "[=== ]",
+ "[ ===]",
+ "[ ==]",
+ "[ =]",
+ "[ ]",
+ "[ =]",
+ "[ ==]",
+ "[ ===]",
+ "[====]",
+ "[=== ]",
+ "[== ]",
+ "[= ]"
+ ]
+ },
+ "bouncingBall": {
+ "interval": 80,
+ "frames": [
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ●)",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "(● )"
+ ]
+ },
+ "smiley": {
+ "interval": 200,
+ "frames": [
+ "😄 ",
+ "😝 "
+ ]
+ },
+ "monkey": {
+ "interval": 300,
+ "frames": [
+ "🙈 ",
+ "🙈 ",
+ "🙉 ",
+ "🙊 "
+ ]
+ },
+ "hearts": {
+ "interval": 100,
+ "frames": [
+ "💛 ",
+ "💙 ",
+ "💜 ",
+ "💚 ",
+ "❤️ "
+ ]
+ },
+ "clock": {
+ "interval": 100,
+ "frames": [
+ "🕛 ",
+ "🕐 ",
+ "🕑 ",
+ "🕒 ",
+ "🕓 ",
+ "🕔 ",
+ "🕕 ",
+ "🕖 ",
+ "🕗 ",
+ "🕘 ",
+ "🕙 ",
+ "🕚 "
+ ]
+ },
+ "earth": {
+ "interval": 180,
+ "frames": [
+ "🌍 ",
+ "🌎 ",
+ "🌏 "
+ ]
+ },
+ "moon": {
+ "interval": 80,
+ "frames": [
+ "🌑 ",
+ "🌒 ",
+ "🌓 ",
+ "🌔 ",
+ "🌕 ",
+ "🌖 ",
+ "🌗 ",
+ "🌘 "
+ ]
+ },
+ "runner": {
+ "interval": 140,
+ "frames": [
+ "🚶 ",
+ "🏃 "
+ ]
+ },
+ "pong": {
+ "interval": 80,
+ "frames": [
+ "▐⠂ ▌",
+ "▐⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂▌",
+ "▐ ⠠▌",
+ "▐ ⡀▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐⠠ ▌"
+ ]
+ },
+ "shark": {
+ "interval": 120,
+ "frames": [
+ "▐|\\____________▌",
+ "▐_|\\___________▌",
+ "▐__|\\__________▌",
+ "▐___|\\_________▌",
+ "▐____|\\________▌",
+ "▐_____|\\_______▌",
+ "▐______|\\______▌",
+ "▐_______|\\_____▌",
+ "▐________|\\____▌",
+ "▐_________|\\___▌",
+ "▐__________|\\__▌",
+ "▐___________|\\_▌",
+ "▐____________|\\▌",
+ "▐____________/|▌",
+ "▐___________/|_▌",
+ "▐__________/|__▌",
+ "▐_________/|___▌",
+ "▐________/|____▌",
+ "▐_______/|_____▌",
+ "▐______/|______▌",
+ "▐_____/|_______▌",
+ "▐____/|________▌",
+ "▐___/|_________▌",
+ "▐__/|__________▌",
+ "▐_/|___________▌",
+ "▐/|____________▌"
+ ]
+ },
+ "dqpb": {
+ "interval": 100,
+ "frames": [
+ "d",
+ "q",
+ "p",
+ "b"
+ ]
+ },
+ "weather": {
+ "interval": 100,
+ "frames": [
+ "☀️ ",
+ "☀️ ",
+ "☀️ ",
+ "🌤 ",
+ "⛅️ ",
+ "🌥 ",
+ "☁️ ",
+ "🌧 ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "⛈ ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "☁️ ",
+ "🌥 ",
+ "⛅️ ",
+ "🌤 ",
+ "☀️ ",
+ "☀️ "
+ ]
+ },
+ "christmas": {
+ "interval": 400,
+ "frames": [
+ "🌲",
+ "🎄"
+ ]
+ },
+ "grenade": {
+ "interval": 80,
+ "frames": [
+ "، ",
+ "′ ",
+ " ´ ",
+ " ‾ ",
+ " ⸌",
+ " ⸊",
+ " |",
+ " ⁎",
+ " ⁕",
+ " ෴ ",
+ " ⁓",
+ " ",
+ " ",
+ " "
+ ]
+ },
+ "point": {
+ "interval": 125,
+ "frames": [
+ "∙∙∙",
+ "●∙∙",
+ "∙●∙",
+ "∙∙●",
+ "∙∙∙"
+ ]
+ },
+ "layer": {
+ "interval": 150,
+ "frames": [
+ "-",
+ "=",
+ "≡"
+ ]
+ }
+})
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/INSTALLER b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/METADATA b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/METADATA
new file mode 100644
index 00000000..20070a73
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/METADATA
@@ -0,0 +1,112 @@
+Metadata-Version: 2.1
+Name: termcolor
+Version: 2.0.1
+Summary: ANSI color formatting for output in terminal
+Project-URL: Changelog, https://github.com/termcolor/termcolor/releases
+Project-URL: Homepage, https://github.com/termcolor/termcolor
+Project-URL: Source, https://github.com/termcolor/termcolor
+Author-email: Konstantin Lepa
+Maintainer: Hugo van Kemenade
+License: MIT
+License-File: COPYING.txt
+Keywords: ANSI,ANSI color,ANSI colour,color,colour,formatting,termcolor,terminal
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Terminals
+Requires-Python: >=3.7
+Provides-Extra: tests
+Requires-Dist: pytest; extra == 'tests'
+Requires-Dist: pytest-cov; extra == 'tests'
+Description-Content-Type: text/markdown
+
+# termcolor
+
+[](https://pypi.org/project/termcolor)
+[](https://pypi.org/project/termcolor)
+[](https://pypistats.org/packages/termcolor)
+[](https://github.com/termcolor/termcolor/actions)
+[](https://codecov.io/gh/termcolor/termcolor)
+[](COPYING.txt)
+[](https://github.com/psf/black)
+
+## Example
+
+```python
+import sys
+
+from termcolor import colored, cprint
+
+text = colored("Hello, World!", "red", attrs=["reverse", "blink"])
+print(text)
+cprint("Hello, World!", "green", "on_red")
+
+print_red_on_cyan = lambda x: cprint(x, "red", "on_cyan")
+print_red_on_cyan("Hello, World!")
+print_red_on_cyan("Hello, Universe!")
+
+for i in range(10):
+ cprint(i, "magenta", end=" ")
+
+cprint("Attention!", "red", attrs=["bold"], file=sys.stderr)
+```
+
+## Text properties
+
+Text colors:
+
+- grey
+- red
+- green
+- yellow
+- blue
+- magenta
+- cyan
+- white
+
+Text highlights:
+
+- on_grey
+- on_red
+- on_green
+- on_yellow
+- on_blue
+- on_magenta
+- on_cyan
+- on_white
+
+Attributes:
+
+- bold
+- dark
+- underline
+- blink
+- reverse
+- concealed
+
+## Terminal properties
+
+| Terminal | bold | dark | underline | blink | reverse | concealed |
+| ------------ | ------- | ---- | --------- | ---------- | ------- | --------- |
+| xterm | yes | no | yes | bold | yes | yes |
+| linux | yes | yes | bold | yes | yes | no |
+| rxvt | yes | no | yes | bold/black | yes | no |
+| dtterm | yes | yes | yes | reverse | yes | yes |
+| teraterm | reverse | no | yes | rev/red | yes | no |
+| aixterm | normal | no | yes | no | yes | yes |
+| PuTTY | color | no | yes | no | yes | no |
+| Windows | no | no | no | no | yes | no |
+| Cygwin SSH | yes | no | color | color | color | yes |
+| Mac Terminal | yes | no | yes | yes | yes | yes |
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/RECORD b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/RECORD
new file mode 100644
index 00000000..69b522b6
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/RECORD
@@ -0,0 +1,10 @@
+termcolor-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+termcolor-2.0.1.dist-info/METADATA,sha256=ICPL4HYUz0sP9Mu9VfkPkFySyaM1qP7F7jfB9hzuG3g,4067
+termcolor-2.0.1.dist-info/RECORD,,
+termcolor-2.0.1.dist-info/WHEEL,sha256=Y57InxZzFBC6JOUn3gj3DWHnWQvSjh7u7UaNLmWRXIQ,86
+termcolor-2.0.1.dist-info/licenses/COPYING.txt,sha256=55tr2CliwTMMqqfEInhWewhmd3dnP44jcaYk1XFdTA4,1072
+termcolor/__init__.py,sha256=Ko_lcHUBKQUPMSCzcfhNwN3kd-AX8fAxajxPbODlG7Q,230
+termcolor/__pycache__/__init__.cpython-310.pyc,,
+termcolor/__pycache__/termcolor.cpython-310.pyc,,
+termcolor/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+termcolor/termcolor.py,sha256=Xn2UxIp3sJbv8FThBHphin6vw5Do16A3JEXt_iCS-7s,5378
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/WHEEL b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/WHEEL
new file mode 100644
index 00000000..8fe858da
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.9.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/licenses/COPYING.txt b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/licenses/COPYING.txt
new file mode 100644
index 00000000..d0b79705
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor-2.0.1.dist-info/licenses/COPYING.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2008-2011 Volvox Development Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/__init__.py b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__init__.py
new file mode 100644
index 00000000..9dceed79
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__init__.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+from termcolor.termcolor import ATTRIBUTES, COLORS, HIGHLIGHTS, RESET, colored, cprint
+
+__all__ = [
+ "ATTRIBUTES",
+ "COLORS",
+ "HIGHLIGHTS",
+ "RESET",
+ "colored",
+ "cprint",
+]
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 00000000..d48fe816
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 00000000..436a5d0f
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/__init__.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-310.pyc b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-310.pyc
new file mode 100644
index 00000000..82a4b3d7
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-310.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-38.pyc b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-38.pyc
new file mode 100644
index 00000000..74016674
Binary files /dev/null and b/provision-contest/ansible/files/admin-python/site-packages/termcolor/__pycache__/termcolor.cpython-38.pyc differ
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/py.typed b/provision-contest/ansible/files/admin-python/site-packages/termcolor/py.typed
new file mode 100644
index 00000000..e69de29b
diff --git a/provision-contest/ansible/files/admin-python/site-packages/termcolor/termcolor.py b/provision-contest/ansible/files/admin-python/site-packages/termcolor/termcolor.py
new file mode 100644
index 00000000..82dae308
--- /dev/null
+++ b/provision-contest/ansible/files/admin-python/site-packages/termcolor/termcolor.py
@@ -0,0 +1,191 @@
+# Copyright (c) 2008-2011 Volvox Development Team
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# Author: Konstantin Lepa
+
+"""ANSI color formatting for output in terminal."""
+
+from __future__ import annotations
+
+import os
+from typing import Any, Iterable
+
+__ALL__ = ["colored", "cprint"]
+
+ATTRIBUTES = dict(
+ list(
+ zip(
+ [
+ "bold",
+ "dark",
+ "",
+ "underline",
+ "blink",
+ "",
+ "reverse",
+ "concealed",
+ ],
+ list(range(1, 9)),
+ )
+ )
+)
+del ATTRIBUTES[""]
+
+
+HIGHLIGHTS = dict(
+ list(
+ zip(
+ [
+ "on_grey",
+ "on_red",
+ "on_green",
+ "on_yellow",
+ "on_blue",
+ "on_magenta",
+ "on_cyan",
+ "on_white",
+ ],
+ list(range(40, 48)),
+ )
+ )
+)
+
+
+COLORS = dict(
+ list(
+ zip(
+ [
+ "grey",
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white",
+ ],
+ list(range(30, 38)),
+ )
+ )
+)
+
+
+RESET = "\033[0m"
+
+
+def colored(
+ text: str,
+ color: str | None = None,
+ on_color: str | None = None,
+ attrs: Iterable[str] | None = None,
+) -> str:
+ """Colorize text.
+
+ Available text colors:
+ grey, red, green, yellow, blue, magenta, cyan, white.
+
+ Available text highlights:
+ on_grey, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white.
+
+ Available attributes:
+ bold, dark, underline, blink, reverse, concealed.
+
+ Example:
+ colored('Hello, World!', 'red', 'on_grey', ['bold', 'blink'])
+ colored('Hello, World!', 'green')
+ """
+ if "NO_COLOR" in os.environ or "ANSI_COLORS_DISABLED" in os.environ:
+ return text
+
+ fmt_str = "\033[%dm%s"
+ if color is not None:
+ text = fmt_str % (COLORS[color], text)
+
+ if on_color is not None:
+ text = fmt_str % (HIGHLIGHTS[on_color], text)
+
+ if attrs is not None:
+ for attr in attrs:
+ text = fmt_str % (ATTRIBUTES[attr], text)
+
+ return text + RESET
+
+
+def cprint(
+ text: str,
+ color: str | None = None,
+ on_color: str | None = None,
+ attrs: Iterable[str] | None = None,
+ **kwargs: Any,
+) -> None:
+ """Print colorize text.
+
+ It accepts arguments of print function.
+ """
+
+ print((colored(text, color, on_color, attrs)), **kwargs)
+
+
+if __name__ == "__main__":
+ print("Current terminal type: %s" % os.getenv("TERM"))
+ print("Test basic colors:")
+ cprint("Grey color", "grey")
+ cprint("Red color", "red")
+ cprint("Green color", "green")
+ cprint("Yellow color", "yellow")
+ cprint("Blue color", "blue")
+ cprint("Magenta color", "magenta")
+ cprint("Cyan color", "cyan")
+ cprint("White color", "white")
+ print("-" * 78)
+
+ print("Test highlights:")
+ cprint("On grey color", on_color="on_grey")
+ cprint("On red color", on_color="on_red")
+ cprint("On green color", on_color="on_green")
+ cprint("On yellow color", on_color="on_yellow")
+ cprint("On blue color", on_color="on_blue")
+ cprint("On magenta color", on_color="on_magenta")
+ cprint("On cyan color", on_color="on_cyan")
+ cprint("On white color", color="grey", on_color="on_white")
+ print("-" * 78)
+
+ print("Test attributes:")
+ cprint("Bold grey color", "grey", attrs=["bold"])
+ cprint("Dark red color", "red", attrs=["dark"])
+ cprint("Underline green color", "green", attrs=["underline"])
+ cprint("Blink yellow color", "yellow", attrs=["blink"])
+ cprint("Reversed blue color", "blue", attrs=["reverse"])
+ cprint("Concealed Magenta color", "magenta", attrs=["concealed"])
+ cprint(
+ "Bold underline reverse cyan color",
+ "cyan",
+ attrs=["bold", "underline", "reverse"],
+ )
+ cprint(
+ "Dark blink concealed white color",
+ "white",
+ attrs=["dark", "blink", "concealed"],
+ )
+ print("-" * 78)
+
+ print("Test mixing:")
+ cprint("Underline red on grey color", "red", "on_grey", ["underline"])
+ cprint("Reversed green on red color", "green", "on_red", ["reverse"])
diff --git a/provision-contest/ansible/files/admin.my.cnf.j2 b/provision-contest/ansible/files/admin.my.cnf.j2
new file mode 100644
index 00000000..7a581112
--- /dev/null
+++ b/provision-contest/ansible/files/admin.my.cnf.j2
@@ -0,0 +1,3 @@
+[mysql]
+user=domjudge
+password={{DB_PASSWORD}}
diff --git a/provision-contest/ansible/files/docs.yaml b/provision-contest/ansible/files/docs.yaml
index 32646e3b..dc4a8fe1 100644
--- a/provision-contest/ansible/files/docs.yaml
+++ b/provision-contest/ansible/files/docs.yaml
@@ -3,18 +3,13 @@
# This file should contain an array with items to show.
+
- name: DOMjudge Team Guide
- link: ../docs/DOMJudge.Team-Manual.WF2021.pdf
+ link: ../docs/WFDhaka-domjudge-team-manual.pdf
icon: fas fa-users
- name: Judging Notes
- link: ../docs/2020-21.JudgingNotes.A4.pdf
+ link: ../docs/2021Dhaka.JudgingNotes.A4.pdf
icon: fas fa-gavel
-- name: Judging Notes Addendum
- link: ../docs/2020-21.JudgingNotes.Addendum.A4.pdf
- icon: fas fa-plus
- name: Tech Notes
- link: ../docs/2020-21.TechNotes.A4.pdf
+ link: ../docs/2021Dhaka.TechNotes.A4.pdf
icon: fas fa-cog
-- name: Mumble Notes
- link: ../docs/2020-21.MumbleNotes.A4.pdf
- icon: fas fa-headset
diff --git a/provision-contest/ansible/files/grafana/nginx.conf b/provision-contest/ansible/files/grafana/nginx.conf
deleted file mode 100644
index 18b5d666..00000000
--- a/provision-contest/ansible/files/grafana/nginx.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-server {
- listen 8443 ssl http2;
- location / {
- proxy_set_header Host $host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- # Expose grafana
- proxy_pass http://127.0.0.1:3000/;
- proxy_read_timeout 90;
- }
- access_log /var/log/nginx/prometheus.log;
- error_log /var/log/nginx/prometheus.log;
-
- ssl_certificate /etc/ssl/certs/grafana.crt;
- ssl_certificate_key /etc/ssl/private/grafana.key;
- ssl_session_timeout 5m;
- ssl_prefer_server_ciphers on;
-}
diff --git a/provision-contest/ansible/files/nicelog b/provision-contest/ansible/files/nicelog
new file mode 100755
index 00000000..1c83db8a
--- /dev/null
+++ b/provision-contest/ansible/files/nicelog
@@ -0,0 +1,6 @@
+awk '
+/(request|app)\.INFO/ {print "\033[36m" $0 "\033[39m"}
+/(request|app)\.WARNING/ {print "\033[33m" $0 "\033[39m"}
+/(request|app)\.ERROR/ {print "\033[31m" $0 "\033[39m"}
+/(request|app)\.CRITICAL/ {print "\033[31m" $0 "\033[39m"}
+'
diff --git a/provision-contest/ansible/files/taillog-domserver-symfony-error.desktop.j2 b/provision-contest/ansible/files/taillog-domserver-symfony-error.desktop.j2
index e1bb4ee2..61325a00 100644
--- a/provision-contest/ansible/files/taillog-domserver-symfony-error.desktop.j2
+++ b/provision-contest/ansible/files/taillog-domserver-symfony-error.desktop.j2
@@ -1,4 +1,4 @@
[Desktop Entry]
Name=taillog-domserver-symfony-error
Type=Application
-Exec=gnome-terminal --window --geometry 74x17+1000+400 -e 'bash -c "tail -F {{ DJ_DIR }}/webapp/var/log/prod.log"'
+Exec=gnome-terminal --window --geometry 74x17+1000+400 -e 'bash -c "tail -n 1000 -F {{ DJ_DIR }}/webapp/var/log/prod.log | ~/bin/nicelog"'
diff --git a/provision-contest/ansible/files/taillog.desktop.j2 b/provision-contest/ansible/files/taillog.desktop.j2
index 6b9b6f74..ec4cbf24 100644
--- a/provision-contest/ansible/files/taillog.desktop.j2
+++ b/provision-contest/ansible/files/taillog.desktop.j2
@@ -1,4 +1,4 @@
[Desktop Entry]
Name=taillog
Type=Application
-Exec=gnome-terminal --window --full-screen -e 'bash -c "tail -f {{ DJ_DIR }}/output/log/*-0.log"'
+Exec=gnome-terminal --window --full-screen -e 'bash -c "tail -f {{ DJ_DIR }}/output/log/*-*.log"'
diff --git a/provision-contest/ansible/generate_passwords.py b/provision-contest/ansible/generate_passwords.py
new file mode 100644
index 00000000..68451dad
--- /dev/null
+++ b/provision-contest/ansible/generate_passwords.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+# Script based on: https://pypi.org/project/xkcdpass/
+from xkcdpass import xkcd_password as xp
+import re, os
+
+words = xp.generate_wordlist(wordfile=xp.locate_wordfile(), min_length=3, max_length=8)
+
+for root,_,files in os.walk('group_vars'):
+ out = ''
+ if '/' not in root:
+ continue
+ if 'secret.yml.example' not in files:
+ continue
+ if os.path.isfile(os.path.join(root, 'secret.yml')):
+ print("Secret file exists already, exiting.")
+ exit(-1)
+ with open(os.path.join(root, 'secret.yml.example'), 'r') as fi:
+ for line in fi:
+ if '{' in line:
+ parms = str(re.search('{.*}', line).group())[1:-1]
+ if '-' in parms:
+ if 'strong' in parms:
+ vls = {parms: xp.generate_xkcdpassword(words, numwords=5, delimiter='-')}
+ else:
+ vls = {parms: xp.generate_xkcdpassword(words, numwords=3, delimiter='-')}
+ out += line.format(**vls)
+ else:
+ out += line
+ with open(os.path.join(root, 'secret.yml'), 'w') as fo:
+ fo.write(out)
diff --git a/provision-contest/ansible/glitchtip.yml b/provision-contest/ansible/glitchtip.yml
new file mode 100644
index 00000000..a9673f7c
--- /dev/null
+++ b/provision-contest/ansible/glitchtip.yml
@@ -0,0 +1,21 @@
+---
+- hosts: glitchtip
+ vars:
+ host_type: glitchtip
+ become: true
+ handlers:
+ - include_tasks: handlers.yml
+ roles:
+ - role: base_packages
+ tags: base_packages
+ - role: icpc_fixes
+ tags: icpc_fixes
+ when: ICPC_IMAGE
+ - role: system_fixes
+ tags: system_fixes
+ - role: hosts
+ tags: hosts
+ - role: docker
+ tags: docker
+ - role: glitchtip
+ tags: glitchtip
diff --git a/provision-contest/ansible/grafana.yml b/provision-contest/ansible/grafana.yml
index e7ab4b67..59d24f8d 100644
--- a/provision-contest/ansible/grafana.yml
+++ b/provision-contest/ansible/grafana.yml
@@ -4,6 +4,8 @@
vars:
host_type: grafana
become: true
+ handlers:
+ - import_tasks: handlers.yml
roles:
- role: base_packages
tags: base_packages
diff --git a/provision-contest/ansible/group_vars/admin.yml b/provision-contest/ansible/group_vars/admin.yml
index ae8cc316..ca98b241 100644
--- a/provision-contest/ansible/group_vars/admin.yml
+++ b/provision-contest/ansible/group_vars/admin.yml
@@ -1,5 +1,7 @@
DJ_DIR: /home/domjudge/domjudge-checkout
+DJ_FORCE_UPDATE: false
+
DOMSERVER_URL: https://localhost
PHP_FPM_MAX_CHILDREN: 5
diff --git a/provision-contest/ansible/group_vars/all/secret.yml.example b/provision-contest/ansible/group_vars/all/secret.yml.example
deleted file mode 100644
index b166b42b..00000000
--- a/provision-contest/ansible/group_vars/all/secret.yml.example
+++ /dev/null
@@ -1,60 +0,0 @@
-# Password for the MySQL replication user.
-# Set this to enable master-master replication between two domservers.
-#REPLICATION_PASSWORD: some-replication-password
-
-# Database user password.
-DB_PASSWORD: some-database-password
-
-# Credentials for the REST API.
-API_USER: judgehost
-API_PASSWORD: some-judgehost-password
-
-# Username and password to be used in .netrc files on admin machines
-ADMIN_USER: admin
-ADMIN_PASSWORD: some-admin-password
-
-# Password for domjudge shell user
-# Set this to enable a password on the 'domjudge' shell accounts
-# created on the domserver and judgehosts.
-#DJ_SHELL_USER_PW: some-hashed-password
-
-# Accounts to create when setting up the CDS
-CDS_ACCOUNTS:
- - username: admin
- password: adm1n
- type: admin
- - username: presAdmin
- password: padm1n
- type: admin
- - username: blue
- password: blu3
- type: staff
- - username: balloon
- password: balloonPr1nter
- type: balloon
- - username: public
- password: publ1c
- type: public
- - username: presentation
- password: presentat1on
- type: public
- - username: myicpc
- password: my1cpc
- type: spectator
- - username: live
- password: l1ve
- type: analyst
- - username: team1
- password: t3am
- type: team
- team_id: 1
-
-# Contest(s) to configure in the CDS
-CDS_CONTESTS:
- - path: nwerc18 # Path in the contest directory
- ccs:
- id: nwerc18 # ID of the contest if hosted at DOMJUDGE_URL
- # Or provide a absolute URL
- # url: https://www.domjudge.org/demoweb/api/contests/nwerc18
- username: admin
- password: admin
diff --git a/provision-contest/ansible/group_vars/domserver.yml b/provision-contest/ansible/group_vars/domserver.yml
new file mode 100644
index 00000000..f71da846
--- /dev/null
+++ b/provision-contest/ansible/group_vars/domserver.yml
@@ -0,0 +1,2 @@
+# If set, configure the real_ip_from header in NGINX to trust a proxy
+# FRONT_REVERSE_PROXY: 192.168.0.1
diff --git a/provision-contest/ansible/group_vars/all/.gitignore b/provision-contest/ansible/group_vars/online/.gitignore
similarity index 57%
rename from provision-contest/ansible/group_vars/all/.gitignore
rename to provision-contest/ansible/group_vars/online/.gitignore
index 769fbcc2..c35135b1 100644
--- a/provision-contest/ansible/group_vars/all/.gitignore
+++ b/provision-contest/ansible/group_vars/online/.gitignore
@@ -1 +1,2 @@
/secret.yml
+/all.yml
diff --git a/provision-contest/ansible/group_vars/online/all.yml.example b/provision-contest/ansible/group_vars/online/all.yml.example
new file mode 120000
index 00000000..cb36e6fe
--- /dev/null
+++ b/provision-contest/ansible/group_vars/online/all.yml.example
@@ -0,0 +1 @@
+../onprem/all.yml.example
\ No newline at end of file
diff --git a/provision-contest/ansible/group_vars/online/secret.yml.example b/provision-contest/ansible/group_vars/online/secret.yml.example
new file mode 120000
index 00000000..66dd4b02
--- /dev/null
+++ b/provision-contest/ansible/group_vars/online/secret.yml.example
@@ -0,0 +1 @@
+../onprem/secret.yml.example
\ No newline at end of file
diff --git a/provision-contest/ansible/group_vars/onprem/.gitignore b/provision-contest/ansible/group_vars/onprem/.gitignore
new file mode 100644
index 00000000..c35135b1
--- /dev/null
+++ b/provision-contest/ansible/group_vars/onprem/.gitignore
@@ -0,0 +1,2 @@
+/secret.yml
+/all.yml
diff --git a/provision-contest/ansible/group_vars/all/all.yml b/provision-contest/ansible/group_vars/onprem/all.yml.example
similarity index 56%
rename from provision-contest/ansible/group_vars/all/all.yml
rename to provision-contest/ansible/group_vars/onprem/all.yml.example
index 47de5730..790b227c 100644
--- a/provision-contest/ansible/group_vars/all/all.yml
+++ b/provision-contest/ansible/group_vars/onprem/all.yml.example
@@ -4,11 +4,14 @@ DJ_DIR: /opt/domjudge
# Branch to checkout and use.
DJ_BRANCH: main
+# Whether to force-pull changes to the checkout, overwriting any local changes.
+DJ_FORCE_UPDATE: true
+
# Server VLAN IP prefix.
SERVER_IP_PREFIX: 10.3.3
-# IP address of the admin machine used as ansible and git server.
-MAIN_ADMIN_IP: "{{SERVER_IP_PREFIX}}.223"
+# IP address of the git server.
+LOCAL_GIT_IP: "{{SERVER_IP_PREFIX}}.207"
# URL and IP of domserver from judgehosts. A hostname 'domserver' with
# DOMSERVER_IP will be added to the judgehost /etc/hosts file.
@@ -21,35 +24,64 @@ DOMSERVER_SSL_KEY: /etc/ssl/private/domserver.key
# Set this to true when you are using the ICPC World Finals Contest Image
ICPC_IMAGE: false
+# Set this to true when you are using a graphical desktop
+GRAPHICAL: false
+
+# Set this to true when you use an (ICPC) AWS machine
+AWS: true
+
# Set this when on the blue network at the World Finals where no
# internet access is available and "packages" must be used as APT repo
# server.
WF_RESTRICTED_NETWORK: false
+WF_GREEN: false
-TIMEZONE: "Europe/Moscow"
+# Static IP address configuration. Uses the ansible_host variable as the static
+# IP address. Only configured if STATIC_IP_ENABLED is true.
+STATIC_IP_ENABLED: false
+STATIC_IP_NETMASK: 255.255.252.0
+STATIC_IP_GATEWAY: 172.29.0.1
+STATIC_IP_INTERFACE: enp1s0
+
+# Additional entries for the /etc/hosts file.
+HOSTS:
+ cds: 10.3.3.207
+ packages: 10.3.3.209
+ ntp1: 10.3.3.208
+ ntp2: 10.3.3.209
+ nisprint: 10.3.3.211
+ nismaster: 10.3.3.211
+ printsrv: 10.3.3.211
+ domjudge-laptop: 10.3.3.200
+ pc2: 10.3.3.241
+
+TIMEZONE: "Europe/Amsterdam"
PHP_FPM_MAX_CHILDREN: 400
PHP_FPM_MAX_REQUESTS: 500
PHP_MEMORY_LIMIT: 1024M
-PHP_UPLOAD_MAX_FILESIZE: 256M
-PHP_POST_MAX_SIZE: 256M
+PHP_UPLOAD_MAX_FILESIZE: 512M
+PHP_POST_MAX_SIZE: 512M
PHP_MAX_FILE_UPLOADS: 101
# Git repo URL
-DJ_GIT_HOST: "{{MAIN_ADMIN_IP}}"
+DJ_GIT_HOST: "{{LOCAL_GIT_IP}}"
DJ_GIT_REPO: "https://github.com/domjudge/domjudge.git"
DJ_GIT_REPO_RESTRICTED: "domjudge@{{DJ_GIT_HOST}}:domjudge"
DJ_GIT_REPO_SCRIPTS: "https://github.com/domjudge/domjudge-scripts.git"
DJ_GIT_REPO_SCRIPTS_RESTRICTED: "domjudge@{{DJ_GIT_HOST}}:domjudge-scripts-bare"
+CONTEST_REPO: "wf2021"
-PHPSTORM_VERSION: 2021.2
-PHPSTORM_FULL_VERSION: 212.5284.49
+PHPSTORM_VERSION: 2022.2
+PHPSTORM_FULL_VERSION: 222.4345.15
GRAFANA_MONITORING: false
# Hostname of the CDS. If set, will add an nginx in front of the CDS
# If not set, will only expose CDS directly
CDS_HOSTNAME: cds
+CDS_PORT: 80
+CDS_PORT_SECURE: 443
# CDS SSL cert and key. Only needed when CDS_HOSTNAME is set
CDS_SSL_CERT: /etc/ssl/certs/cds.crt
@@ -60,3 +92,8 @@ STATIC_SCOREBOARD_HOSTNAME: scoreboard
STATIC_SCOREBOARD_SSL_CERT: /etc/ssl/certs/scoreboard.crt
STATIC_SCOREBOARD_SSL_KEY: /etc/ssl/private/scoreboard.key
+
+# Block access to the CDS for IPs other than these
+#CDS_IP_FILTER:
+# - 127.0.0.1-127.0.0.1
+# - 192.168.0.0-192.168.255.255
diff --git a/provision-contest/ansible/group_vars/onprem/secret.yml.example b/provision-contest/ansible/group_vars/onprem/secret.yml.example
new file mode 100644
index 00000000..bdd179a5
--- /dev/null
+++ b/provision-contest/ansible/group_vars/onprem/secret.yml.example
@@ -0,0 +1,75 @@
+# Templated passwords as `{some-strong-password}` are written to make sure our
+# script detects those, if you manually change those the `{}` are not required and
+# would become part of the password.
+# Adding `strong` in the template will create longer passwords and is used for the
+# passwords which almost never need to be manually typed.
+
+# Password for the MySQL replication user.
+# Set this to enable master-master replication between two domservers.
+#REPLICATION_PASSWORD: {some-strong-replication-password}
+
+# Database user password.
+DB_PASSWORD: {some-strong-database-password}
+
+# Credentials for the judgehost.
+JUDGEHOST_USER: judgehost
+JUDGEHOST_PASSWORD: {some-strong-judgehost-password}
+
+# Username and password to be used in .netrc files on admin machines
+ADMIN_USER: admin
+ADMIN_PASSWORD: {some-admin-password}
+
+# Password for domjudge shell user
+# Set this to enable a password on the 'domjudge' shell accounts
+# created on the domserver and judgehosts.
+#DJ_SHELL_USER_PW: {some-hashed-password}
+
+# Accounts to create when setting up the CDS
+CDS_ACCOUNTS:
+ - username: admin
+ password: {some-adm1n-password}
+ type: admin
+ - username: presAdmin
+ password: {some-presentation-adm1n-password}
+ type: admin
+ - username: presentation
+ password: {some-public-presentation-password}
+ type: public
+ #- username: blue
+ # password: blu3
+ # type: staff
+ #- username: balloon
+ # password: balloonPr1nter
+ # type: balloon
+ #- username: public
+ # password: publ1c
+ # type: public
+ #- username: myicpc
+ # password: my1cpc
+ # type: spectator
+ #- username: live
+ # password: l1ve
+ # type: analyst
+ #- username: team1
+ # password: t3am
+ # type: team
+ # team_id: 1
+
+# Contest(s) to configure in the CDS
+CDS_CONTESTS:
+ - path: nwerc18 # Path in the contest directory
+ ccs:
+ id: nwerc18 # ID of the contest if hosted at DOMJUDGE_URL
+ # Or provide a absolute URL
+ # url: https://www.domjudge.org/demoweb/api/contests/nwerc18
+ username: admin
+ password: admin
+
+PRESCLIENT_CONTEST: nwerc18
+
+# Sentry DSN URL
+# SENTRY_DSN:
+
+# GlitchTip
+# GLITCHTIP_SECRET: {some-strong-glitchtip-password}
+# GLITCHTIP_PASSWORD: {some-strong-glitchtip-password}
diff --git a/provision-contest/ansible/handlers.yml b/provision-contest/ansible/handlers.yml
index e1378e82..3c456cc4 100644
--- a/provision-contest/ansible/handlers.yml
+++ b/provision-contest/ansible/handlers.yml
@@ -1,6 +1,20 @@
# Common definition of handlers
---
-- name: clear application cache
+- name: Clear application cache
command: "{{ DJ_DIR }}/webapp/bin/console cache:clear"
become: true
become_user: domjudge
+
+- name: Rebuild domjudge
+ become: true
+ become_user: domjudge
+ make:
+ chdir: "{{ DJ_DIR }}"
+ target: inplace-install
+ notify: Fix permissions on domjudge inplace-install
+
+- name: Restart nginx
+ systemd:
+ name: nginx
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/hosts b/provision-contest/ansible/hosts.example
similarity index 59%
rename from provision-contest/ansible/hosts
rename to provision-contest/ansible/hosts.example
index d9e25401..837eb59e 100644
--- a/provision-contest/ansible/hosts
+++ b/provision-contest/ansible/hosts.example
@@ -2,10 +2,35 @@
ansible_user=root
ansible_python_interpreter=/usr/bin/python3
+# When moving clients for ad-hoc actions:
+# move them to their own group to keep hosts files on deployed machines in sync.
+
+[onprem:children]
+domserver
+judgehost
+cds
+grafana
+admin
+presclient
+presadmin
+scoreboard
+mgmt
+autoanalyst
+
+[online:children]
+online-domserver
+online-judgehost
+
[domserver]
domjudge-primary ansible_host=10.3.3.216 KEEPALIVED_PRIORITY=100 EFI_ORDER='0\,1\,3\,4'
domjudge-backup ansible_host=10.3.3.217 KEEPALIVED_PRIORITY=99 EFI_ORDER='0\,1\,3\,4'
+[domserver:children]
+emergency
+
+[emergency]
+domjudge-laptop ansible_host=10.3.3.218
+
[judgehost]
domjudge-judgehost1 ansible_host=10.2.2.192
domjudge-judgehost2 ansible_host=10.2.2.193
@@ -34,13 +59,34 @@ domjudge-ccsadmin4 ansible_host=10.3.3.226
domjudge-ccsadmin5 ansible_host=10.3.3.227
[grafana]
-domjudge-prometheus ansible_host=10.3.3.223
+# During the WFs we use one of the ccsadmin machines
+# Doesn't matter which (admin) machine but should not be 1 as that runs ansible
+domjudge-ccsadmin2 ansible_host=10.3.3.224
+
+[glitchtip]
+# During the WFs we use one of the ccsadmin machines
+# Doesn't matter which (admin) machine but should not be 1 as that runs ansible
+domjudge-ccsadmin3 ansible_host=10.3.3.225
[cds]
domjudge-cds ansible_host=10.2.2.228
+[presclient]
+presclient1 ansible_host=10.2.2.230
+
+[presadmin]
+presadmin ansible_host=10.2.2.231
+
[scoreboard]
domjudge-scoreboard ansible_host=10.2.2.229
[mgmt]
domjudge-mgmg ansible_host=10.3.3.223
+
+[autoanalyst]
+
+[online-domserver]
+online-domserver ansible_host=192.168.255.255
+
+[online-judgehost]
+online-judgehost1 ansible_host=192.168.255.255
diff --git a/provision-contest/ansible/judgehost.yml b/provision-contest/ansible/judgehost.yml
index e2f51b07..a26462ab 100644
--- a/provision-contest/ansible/judgehost.yml
+++ b/provision-contest/ansible/judgehost.yml
@@ -1,13 +1,9 @@
---
# This playbook installs the DOMjudge judgehosts
-- name: setup judgehost
- hosts: judgehost
- # We always leave 1/3 of the judges online
- serial:
- - 33%
- - 33%
- - 34%
+- name: Setup judgehost
+ hosts: judgehost,online-judgehost
+ strategy: free
vars:
host_type: judgehost
become: true
@@ -28,45 +24,56 @@
tags: ssl
- role: domjudge_user
tags: domjudge_user
+ - role: ssh
+ tags: ssh
- role: domjudge_checkout
tags: domjudge_checkout
- role: domjudge_build
tags: domjudge_build
- role: judgedaemon
tags: judgedaemon
- - role: ssh
- tags: ssh
- role: domlogo
tags: domlogo
+ when: GRAPHICAL
- role: prometheus_target_all
tags: prometheus_target_all
when: GRAFANA_MONITORING
+ handlers:
+ - import_tasks: handlers.yml
pre_tasks:
- - name: Get current services
- service_facts:
- - name: Disable all cores
+ - name: Check if the service is already available
+ stat:
+ path: /etc/systemd/system/domjudge-judgedaemon@.service
+ get_attributes: false
+ get_checksum: false
+ get_mime: false
+ register: service_file
+ check_mode: no
+ tags: pretask
+
+ - name: Stop judgedaemon service(s) on every possible core
service:
- name="domjudge-judgedaemon@{{ item }}"
- state=stopped
+ name: "{{ item }}"
+ state: stopped
with_sequence: start=0 end={{ ansible_processor_vcpus }} format=domjudge-judgedaemon@%1x
- when: '"domjudge-judgedaemon@0.service" in services'
+ when: service_file.stat.exists
+ tags: pretask
tasks:
- - name: add autostart shortcuts
- template:
- src: files/{{ item }}.desktop
- dest: /home/domjudge/.config/autostart/
- owner: domjudge
- group: domjudge
- mode: 0755
- loop:
- - rotate
+ - name: Create graphical autostart entries
+ when: GRAPHICAL
+ block:
+ - name: Add autostart shortcuts
+ template:
+ src: files/rotate.desktop
+ dest: /home/domjudge/.config/autostart/
+ owner: domjudge
+ group: domjudge
+ mode: 0755
- - name: add autostart shortcuts from template
- template:
- src: files/{{ item }}.desktop.j2
- dest: /home/domjudge/.config/autostart/{{ item }}.desktop
- owner: domjudge
- group: domjudge
- mode: 0755
- loop:
- - taillog
+ - name: Add autostart shortcuts from template
+ template:
+ src: files/taillog.desktop.j2
+ dest: /home/domjudge/.config/autostart/taillog.desktop
+ owner: domjudge
+ group: domjudge
+ mode: 0755
diff --git a/provision-contest/ansible/mgmt.yml b/provision-contest/ansible/mgmt.yml
index dba3333e..62d76794 100644
--- a/provision-contest/ansible/mgmt.yml
+++ b/provision-contest/ansible/mgmt.yml
@@ -1,12 +1,16 @@
---
# This playbook installs the Management machine
-- name: setup management
+- name: Setup management
hosts: mgmt
vars:
host_type: mgmt
become: true
+ handlers:
+ - import_tasks: handlers.yml
roles:
+ - role: base_packages
+ tags: base_packages
- role: prometheus_target_all
tags: prometheus_target_all
when: GRAFANA_MONITORING
diff --git a/provision-contest/ansible/presadmin.yml b/provision-contest/ansible/presadmin.yml
new file mode 100644
index 00000000..3d8f0bba
--- /dev/null
+++ b/provision-contest/ansible/presadmin.yml
@@ -0,0 +1,30 @@
+---
+# This playbook installs the presentation admin
+
+- name: Setup presentation admin
+ hosts: presadmin
+ vars:
+ host_type: presadmin
+ GRAPHICAL: true
+ become: true
+ handlers:
+ - import_tasks: handlers.yml
+ roles:
+ - role: base_packages
+ tags: base_packages
+ - role: icpc_fixes
+ tags: icpc_fixes
+ when: ICPC_IMAGE
+ - role: system_fixes
+ tags: system_fixes
+ - role: hosts
+ tags: hosts
+ - role: domjudge_user
+ tags: domjudge_user
+ - role: ssh
+ tags: ssh
+ - role: presadmin
+ tags: presadmin
+ - role: prometheus_target_all
+ tags: prometheus_target_all
+ when: GRAFANA_MONITORING
diff --git a/provision-contest/ansible/presclient.yml b/provision-contest/ansible/presclient.yml
new file mode 100644
index 00000000..a3334aff
--- /dev/null
+++ b/provision-contest/ansible/presclient.yml
@@ -0,0 +1,30 @@
+---
+# This playbook installs the presentation client
+
+- name: Setup presentation client
+ hosts: presclient
+ vars:
+ host_type: presclient
+ GRAPHICAL: true
+ become: true
+ handlers:
+ - import_tasks: handlers.yml
+ roles:
+ - role: base_packages
+ tags: base_packages
+ - role: icpc_fixes
+ tags: icpc_fixes
+ when: ICPC_IMAGE
+ - role: system_fixes
+ tags: system_fixes
+ - role: hosts
+ tags: hosts
+ - role: domjudge_user
+ tags: domjudge_user
+ - role: ssh
+ tags: ssh
+ - role: presclient
+ tags: presclient
+ - role: prometheus_target_all
+ tags: prometheus_target_all
+ when: GRAFANA_MONITORING
diff --git a/provision-contest/ansible/roles/autoanalyst/handlers/main.yml b/provision-contest/ansible/roles/autoanalyst/handlers/main.yml
new file mode 100644
index 00000000..6dfcdd76
--- /dev/null
+++ b/provision-contest/ansible/roles/autoanalyst/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: Reload nginx
+ systemd:
+ name: nginx
+ state: reloaded
diff --git a/provision-contest/ansible/roles/autoanalyst/tasks/main.yml b/provision-contest/ansible/roles/autoanalyst/tasks/main.yml
new file mode 100644
index 00000000..7eaf142f
--- /dev/null
+++ b/provision-contest/ansible/roles/autoanalyst/tasks/main.yml
@@ -0,0 +1,199 @@
+---
+- name: Create the analyst user
+ user:
+ name: analyst
+ groups: [sudo]
+ state: present
+ shell: /bin/bash
+
+- name: Check if autoanalyst repository exists
+ stat:
+ path: /home/analyst/autoanalyst
+ register: repo_directory
+
+- name: Clone the autoanalyst repository
+ become_user: analyst
+ git:
+ repo: 'https://github.com/icpc-live/autoanalyst.git'
+ dest: /home/analyst/autoanalyst
+ when: not repo_directory.stat.exists
+
+- name: Create directories
+ become_user: analyst
+ file:
+ path: /home/analyst//{{ item }}
+ state: directory
+ with_items:
+ - githomes
+ - backup
+ - history
+ - autoanalyst/output
+
+- name: Install necessary packages
+ apt:
+ name:
+ - git
+ - gitk
+ - gitweb
+ - apache2-utils
+ - mariadb-server
+ - mariadb-client
+ - php-cli
+ - ntp
+ - rsync
+ - make
+ - curl
+ - python-yaml
+ - openjdk-8-jdk
+ - openjdk-8-jre
+ - python-httplib2
+ - python3-mysqldb
+ - maven
+
+- name: Check if configuration exists
+ stat:
+ path: /home/analyst/autoanalyst/config.yaml
+ register: config_file
+
+- name: Create initial configuration
+ become_user: analyst
+ copy:
+ src: /home/analyst/autoanalyst/config.yaml.template
+ dest: /home/analyst/autoanalyst/config.yaml
+ when: not config_file.stat.exists
+
+- name: Create initial configuration
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ regexp: /home/icpclive
+ replace: /home/analyst
+ when: not config_file.stat.exists
+
+- name: Configure CCS baseurl
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'CCS:'
+ regexp: 'baseurl:.*'
+ replace: 'baseurl: "https://domjudge/"'
+ before: 'CDS:'
+ when: not config_file.stat.exists
+
+- name: Configure CCS submissionurl
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'CCS:'
+ regexp: 'submissionurl:.*'
+ replace: 'submissionurl: "jury/submissions/by-external-id/@ID@"'
+ before: 'CDS:'
+ when: not config_file.stat.exists
+
+- name: Configure CDS baseurl
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'CDS:'
+ regexp: 'baseurl:.*'
+ replace: 'baseurl: "https://cds/api/contests"'
+ before: 'teambackup:'
+ when: not config_file.stat.exists
+
+- name: Configure CDS user
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'CDS:'
+ regexp: 'user:.*'
+ replace: 'user: analytics'
+ before: 'teambackup:'
+ when: not config_file.stat.exists
+
+- name: Configure CDS user
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'CDS:'
+ regexp: 'userfull:.*'
+ replace: 'userfull: analyticsplus'
+ before: 'teambackup:'
+ when: not config_file.stat.exists
+
+- name: Configure database user
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'database:'
+ regexp: 'user:.*'
+ replace: 'user: icat'
+ before: 'timezone:'
+ when: not config_file.stat.exists
+
+- name: Enable file publisher
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: '# File publisher'
+ regexp: 'enable:.*'
+ replace: 'enable: true'
+ before: 'targetDirectory:'
+ when: not config_file.stat.exists
+
+- name: Generate database password
+ set_fact:
+ database_pass: '{{ lookup("password", "/dev/null length=15 chars=ascii_letters") }}'
+
+- name: Configure database password
+ become_user: analyst
+ replace:
+ path: /home/analyst/autoanalyst/config.yaml
+ after: 'database:'
+ regexp: 'password:.*'
+ replace: 'password: {{ database_pass }}'
+ before: 'teambackup:'
+ when: not config_file.stat.exists
+
+- name: Configure timezone
+ become_user: analyst
+ lineinfile:
+ path: /home/analyst/autoanalyst/config.yaml
+ regexp: '^timezone:'
+ line: 'timezone: Asia/Dhaka'
+ when: not config_file.stat.exists
+
+- name: Create database user
+ pause:
+ prompt: "mysql> CREATE USER 'icat'@'localhost' IDENTIFIED BY '{{ database_pass }}';"
+
+- name: Create database
+ pause:
+ prompt: "mysql> CREATE DATABASE icat;"
+
+- name: Grant database user privileges
+ pause:
+ prompt: "mysql> GRANT ALL PRIVILEGES ON icat.* TO 'icat'@'localhost';"
+
+- name: Populate database
+ pause:
+ prompt: "mysql -u icat --password={{ database_pass }} -h 127.0.0.1 < /home/analyst/autoanalyst/create_icat_instance.sql"
+
+- name: Create the nginx configuration
+ template:
+ src: nginx.conf.j2
+ dest: /etc/nginx/sites-available/autoanalyst.conf
+ notify: Reload nginx
+
+- name: Create the nginx configuration
+ file:
+ src: /etc/nginx/sites-available/autoanalyst.conf
+ dest: /etc/nginx/sites-enabled/autoanalyst.conf
+ state: link
+ notify: Reload nginx
+
+- name: Build the Katalyzer
+ become_user: analyst
+ command: mvn package
+ args:
+ chdir: /home/analyst/autoanalyst/katalyze
+ creates: /home/analyst/autoanalyst/katalyze/target
diff --git a/provision-contest/ansible/roles/autoanalyst/templates/nginx.conf.j2 b/provision-contest/ansible/roles/autoanalyst/templates/nginx.conf.j2
new file mode 100644
index 00000000..b6d41232
--- /dev/null
+++ b/provision-contest/ansible/roles/autoanalyst/templates/nginx.conf.j2
@@ -0,0 +1,34 @@
+server {
+ listen 80;
+ server_name icat;
+ return 308 https://$host$request_uri; # enforce https
+}
+
+server {
+ listen 443 ssl;
+ server_name icat;
+
+ ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
+ ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
+ ssl_session_timeout 5m;
+ ssl_prefer_server_ciphers on;
+
+ send_timeout 36000s;
+ root /home/analyst/autoanalyst/www;
+
+ location ~ ^/icat/api/(EventFeed|scoreboard|teams)$ {
+ rewrite ^/icat/api/(EventFeed|scoreboard|teams)$ /$1 break;
+ proxy_pass http://localhost:8099;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ }
+
+ location ~ \.php$ {
+ include snippets/fastcgi-php.conf;
+
+ # With php-fpm (or other unix sockets):
+ fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
+ # With php-cgi (or other tcp sockets):
+ #fastcgi_pass 127.0.0.1:9000;
+ }
+}
diff --git a/provision-contest/ansible/roles/base_packages/defaults/main.yml b/provision-contest/ansible/roles/base_packages/defaults/main.yml
new file mode 100644
index 00000000..6a552323
--- /dev/null
+++ b/provision-contest/ansible/roles/base_packages/defaults/main.yml
@@ -0,0 +1,55 @@
+REMOVED_PACKAGES:
+ - apport
+ - ntp
+
+INSTALLED_PACKAGES:
+ - ack
+ - acl
+ - apache2-utils
+ - autoconf
+ - automake
+ - bat
+ - bats
+ - bsdmainutils
+ - composer
+ - curl
+ - debootstrap
+ - default-jdk-headless
+ - efibootmgr
+ - etckeeper
+ - fontconfig
+ - g++
+ - gcc
+ - git
+ - git-gui
+ - gitk
+ - htop
+ - httpie
+ - jq
+ - latexmk
+ - libcgroup-dev
+ - libcurl4-gnutls-dev
+ - libjsoncpp-dev
+ - libmagic-dev
+ - make
+ - ncdu
+ - php-cli
+ - php-curl
+ - php-gd
+ - php-intl
+ - php-json
+ - php-mbstring
+ - php-mysql
+ - php-xml
+ - php-zip
+ - php-bcmath
+ - pv
+ - python3-sphinx
+ - python3-sphinx-rtd-theme
+ - python3-yaml
+ - rst2pdf
+ - screen
+ - tig
+ - tree
+ - unzip
+ - zip
diff --git a/provision-contest/ansible/roles/base_packages/files/install-docker/.gitignore b/provision-contest/ansible/roles/base_packages/files/install-docker/.gitignore
new file mode 100644
index 00000000..c00df136
--- /dev/null
+++ b/provision-contest/ansible/roles/base_packages/files/install-docker/.gitignore
@@ -0,0 +1 @@
+*.deb
diff --git a/provision-contest/ansible/roles/base_packages/handlers/main.yml b/provision-contest/ansible/roles/base_packages/handlers/main.yml
index 81c2578c..5230f6b8 100644
--- a/provision-contest/ansible/roles/base_packages/handlers/main.yml
+++ b/provision-contest/ansible/roles/base_packages/handlers/main.yml
@@ -1,5 +1,4 @@
---
-# Define here handlers associated to this role.
-
-- name: run apt update
- command: apt update
+- name: Run apt update
+ apt:
+ update_cache: true
diff --git a/provision-contest/ansible/roles/base_packages/tasks/install-local-package.yml b/provision-contest/ansible/roles/base_packages/tasks/install-local-package.yml
index 180b53a3..65a49aaa 100644
--- a/provision-contest/ansible/roles/base_packages/tasks/install-local-package.yml
+++ b/provision-contest/ansible/roles/base_packages/tasks/install-local-package.yml
@@ -1,6 +1,6 @@
# Install a single local DEB package.
---
-- name: create temp package directory
+- name: Create temp package directory
file:
path: /tmp/dj_ansible
state: directory
@@ -8,12 +8,12 @@
group: root
mode: 0700
-- name: copy DEB package to remote
+- name: Copy DEB package to remote
synchronize:
src: "{{ item }}"
- dest: "/tmp/dj_ansible/{{ item|basename }}"
+ dest: "/tmp/dj_ansible/{{ item | basename }}"
-- name: install DEB package {{ item|basename }}
+- name: Install DEB package {{ item | basename }}
apt:
- deb: "/tmp/dj_ansible/{{ item|basename }}"
+ deb: "/tmp/dj_ansible/{{ item | basename }}"
state: present
diff --git a/provision-contest/ansible/roles/base_packages/tasks/main.yml b/provision-contest/ansible/roles/base_packages/tasks/main.yml
index 2fede9ff..de4b0175 100644
--- a/provision-contest/ansible/roles/base_packages/tasks/main.yml
+++ b/provision-contest/ansible/roles/base_packages/tasks/main.yml
@@ -1,128 +1,63 @@
---
# This task configures packaging and installs various system utilities
-- name: replace pc2.ecs.baylor.edu by packages in apt sources
- replace:
- dest: "/etc/apt/{{ item }}"
- regexp: 'pc2\.ecs\.baylor\.edu'
- replace: 'packages'
- loop:
- - sources.list
- - sources.list.d/microsoft.list
- - sources.list.d/mono.list
- - sources.list.d/vscode.list
- - sources.list.d/pypy-ubuntu-ppa-buster.list
- notify: run apt update
- when: WF_RESTRICTED_NETWORK
-
-- name: add packages to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: '^10\.3\.3\.209'
- line: "10.3.3.209 packages"
- notify: run apt update
- when: WF_RESTRICTED_NETWORK
-
-- name: check for dpkg architecture i386
+- name: World finals fixes for apt sources
+ block:
+ - name: Replace pc2.ecs.baylor.edu by packages in apt sources
+ replace:
+ dest: "/etc/apt/{{ item }}"
+ regexp: 'sysopspackages\.icpc\.global'
+ replace: 'packages'
+ loop:
+ - sources.list
+ - sources.list.d/mono.list
+ - sources.list.d/vscode.list
+ - sources.list.d/pypy-ubuntu-ppa-jammy.list
+ notify: Run apt update
+
+ - name: Add packages to hosts file
+ lineinfile:
+ dest: /etc/hosts
+ line: "{{ HOSTS['packages'] }} packages"
+ notify: Run apt update
+
+ - name: Remove pycharm repo
+ replace:
+ dest: /etc/apt/sources.list
+ regexp: '^([^#].*pycharm.*)$'
+ replace: '# \1'
+ notify: Run apt update
+ when: ICPC_IMAGE and WF_RESTRICTED_NETWORK
+
+- name: Check for dpkg architecture i386
command: dpkg --print-foreign-architectures
register: dpkg_architectures
changed_when: false
+ check_mode: no
-- name: remove unused dpkg architecture i386
+- name: Remove unused dpkg architecture i386
command: dpkg --remove-architecture i386
- notify: run apt update
+ notify: Run apt update
when: dpkg_architectures.stdout.find('i386') != -1
-- name: remove pycharm repo
- replace:
- dest: /etc/apt/sources.list
- regexp: '^([^#].*pycharm.*)$'
- replace: '# \1'
- notify: run apt update
+- name: Update cache if this is our first run
+ apt:
+ cache_valid_time: 3600
-- name: flush handlers
+- name: Flush handlers
meta: flush_handlers
-- name: remove some packages
+- name: Remove some packages
apt:
state: absent
- pkg:
- - apport
- - ntp
+ pkg: "{{ REMOVED_PACKAGES }}"
-- name: install common required/useful packages
+- name: Install common required/useful packages
apt:
state: present
- pkg:
- - ack
- - git
- - htop
- - httpie
- - ncdu
- - pv
- - screen
- - autoconf
- - automake
- - efibootmgr
- - curl
- - gcc
- - g++
- - default-jdk-headless
- - make
- - zip
- - unzip
- - php-cli
- - php-gd
- - php-curl
- - php-mysql
- - php-json
- - php-xml
- - php-zip
- - php-mbstring
- - php-intl
- - bsdmainutils
- - libcgroup-dev
- - libcurl4-gnutls-dev
- - libjsoncpp-dev
- - libmagic-dev
- - debootstrap
- - texlive-latex-recommended
- - texlive-latex-extra
- - apache2-utils
- - tig
- - bat
- - jq
- - python3-sphinx
- - autoconf
- - automake
- - bats
- - python3-sphinx
- - python3-sphinx-rtd-theme
- - rst2pdf
- - fontconfig
- - python3-yaml
- - latexmk
- - acl
-
-- name: Check if composer is installed
- stat:
- path: /usr/local/bin/composer
- register: composer_file
-
-- name: Download Composer Installer
- get_url:
- url: https://getcomposer.org/installer
- dest: /root/composer-setup.php
- owner: root
- group: root
- mode: 0755
- when: not composer_file.stat.exists
-
-- name: Install composer
- command: php /root/composer-setup.php --install-dir=/usr/local/bin --filename=composer
- when: not composer_file.stat.exists
+ pkg: "{{ INSTALLED_PACKAGES }}"
-- name: install local DEB packages
- include: install-local-package.yml
+- name: Install local DEB packages
+ include_tasks: install-local-package.yml
with_fileglob:
- install-{{ host_type }}/*.deb
diff --git a/provision-contest/ansible/roles/base_packages/vars/.gitignore b/provision-contest/ansible/roles/base_packages/vars/.gitignore
new file mode 100644
index 00000000..1cda54be
--- /dev/null
+++ b/provision-contest/ansible/roles/base_packages/vars/.gitignore
@@ -0,0 +1 @@
+*.yml
diff --git a/provision-contest/ansible/roles/cds/files/cds.service b/provision-contest/ansible/roles/cds/files/cds.service
index a1e6e7b4..a1b24a1e 100644
--- a/provision-contest/ansible/roles/cds/files/cds.service
+++ b/provision-contest/ansible/roles/cds/files/cds.service
@@ -4,6 +4,7 @@ Description=CDS
User=domjudge
Restart=always
ExecStart=/home/domjudge/cds/wlp/bin/server run cds
+Environment=JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
TimeoutStopSec=20s
[Install]
WantedBy=multi-user.target
diff --git a/provision-contest/ansible/roles/cds/handlers/main.yml b/provision-contest/ansible/roles/cds/handlers/main.yml
index 71fde60f..dae2b91d 100644
--- a/provision-contest/ansible/roles/cds/handlers/main.yml
+++ b/provision-contest/ansible/roles/cds/handlers/main.yml
@@ -1,16 +1,13 @@
---
-# Define here handlers associated to this role.
-
-- name: restart cds
+- name: Restart cds
systemd:
name: cds
enabled: true
state: restarted
daemon_reload: true
-- name: restart nginx
+- name: Restart nginx
systemd:
name: nginx
enabled: true
state: restarted
- daemon_reload: true
diff --git a/provision-contest/ansible/roles/cds/tasks/main.yml b/provision-contest/ansible/roles/cds/tasks/main.yml
index e55f112f..db155a0b 100644
--- a/provision-contest/ansible/roles/cds/tasks/main.yml
+++ b/provision-contest/ansible/roles/cds/tasks/main.yml
@@ -1,6 +1,11 @@
---
# These tasks configure the CDS
+- name: Install OpenJDK17
+ apt:
+ name:
+ - openjdk-17-jre
+
- name: Get the CDS release
uri:
url: https://api.github.com/repos/icpctools/icpctools/releases?per_page=1
@@ -9,6 +14,7 @@
status_code: 200
body_format: json
register: latest_cds_release_array
+ check_mode: no
- name: Set CDS latest version
set_fact:
@@ -22,8 +28,9 @@
stat:
path: /home/domjudge/cds/wlp/usr/servers/cds/apps/CDS.war
register: cds_war
+ check_mode: no
-- name: create CDS directory
+- name: Create CDS directory
file:
path: /home/domjudge/cds
state: directory
@@ -39,7 +46,7 @@
owner: domjudge
group: domjudge
when: not cds_war.stat.exists
- notify: restart cds
+ notify: Restart cds
- name: Download and unpack CDS WAR
unarchive:
@@ -57,7 +64,7 @@
group: domjudge
mode: 0644
when: cds_war.stat.exists
- notify: restart cds
+ notify: Restart cds
- name: Populate CDS accounts.yaml
template:
@@ -66,7 +73,7 @@
owner: domjudge
group: domjudge
mode: 0600
- notify: restart cds
+ notify: Restart cds
- name: Populate CDS cdsConfig.xml
template:
@@ -75,7 +82,7 @@
owner: domjudge
group: domjudge
mode: 0600
- notify: restart cds
+ notify: Restart cds
- name: Create contests config directory
file:
@@ -94,41 +101,41 @@
mode: 0755
loop: "{{ CDS_CONTESTS }}"
-- name: copy cds systemd unit file
+- name: Copy cds systemd unit file
copy:
src: cds.service
dest: /etc/systemd/system/
mode: 0644
notify:
- - restart cds
+ - Restart cds
- name: Setup nginx
block:
- - name: install nginx
+ - name: Install nginx
apt:
state: present
pkg:
- nginx
- - name: add CDS nginx conf
+ - name: Add CDS nginx conf
template:
src: cds.conf.j2
dest: /etc/nginx/sites-available/cds.conf
mode: 0644
group: root
owner: root
- notify: restart nginx
+ notify: Restart nginx
- - name: enable nginx conf for CDS
+ - name: Enable nginx conf for CDS
file:
src: /etc/nginx/sites-available/cds.conf
dest: /etc/nginx/sites-enabled/cds.conf
state: link
- notify: restart nginx
+ notify: Restart nginx
- - name: disable default nginx site
+ - name: Disable default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
- notify: restart nginx
+ notify: Restart nginx
when: CDS_HOSTNAME is defined
diff --git a/provision-contest/ansible/roles/cds/templates/cds.conf.j2 b/provision-contest/ansible/roles/cds/templates/cds.conf.j2
index 13dd26fb..94abc183 100644
--- a/provision-contest/ansible/roles/cds/templates/cds.conf.j2
+++ b/provision-contest/ansible/roles/cds/templates/cds.conf.j2
@@ -1,16 +1,27 @@
# nginx configuration for the CDS
+
+{% if CDS_IP_FILTER is defined %}
+geo $network {
+ ranges;
+ default BLOCK;
+{% for range in CDS_IP_FILTER %}
+ {{ range }} ALLOW;
+{% endfor %}
+}
+{% endif %}
+
server {
- listen 80;
- listen [::]:80;
+ listen {{ CDS_PORT }};
+ listen [::]:{{ CDS_PORT }};
server_name {{CDS_HOSTNAME}};
- return 301 https://$host$request_uri;
+ return 301 https://$host$request_uri:{{ CDS_PORT_SECURE }};
}
server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
+ listen {{ CDS_PORT_SECURE }} ssl http2;
+ listen [::]:{{ CDS_PORT_SECURE }} ssl http2;
server_name {{CDS_HOSTNAME}};
@@ -26,12 +37,20 @@ server {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
- proxy_pass https://localhost:8443;
+ {% if CDS_IP_FILTER is defined %}
+ if ( $network = ALLOW) {
+ proxy_pass https://localhost:8443;
+ }
+ {% else %}
+ proxy_pass https://localhost:8443;
+ {% endif %}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_request_buffering off;
proxy_buffering off;
+ # CDS event feed sends a heartbeat every 120s, so make sure our timeout is longer
+ proxy_read_timeout 130s;
}
}
diff --git a/provision-contest/ansible/roles/clusterssh/tasks/main.yml b/provision-contest/ansible/roles/clusterssh/tasks/main.yml
index b9d7f783..585432da 100644
--- a/provision-contest/ansible/roles/clusterssh/tasks/main.yml
+++ b/provision-contest/ansible/roles/clusterssh/tasks/main.yml
@@ -1,32 +1,24 @@
---
# These tasks configure clusterssh
-- name: install clusterssh package
+- name: Install clusterssh package
apt:
state: present
pkg:
- clusterssh
-- name: create clusterssh 'all' config group
- become: true
- become_user: domjudge
- lineinfile:
- dest: /home/domjudge/.clusterssh/clusters
- regexp: '^all'
- line: "all {{ groups['all'] | join(' ') }}"
- create: true
- mode: 0644
+- name: Create clusterssh clusters file
+ file:
+ state: directory
+ owner: domjudge
+ group: domjudge
+ path: /home/domjudge/.clusterssh
+ mode: 0755
-- name: create clusterssh config groups
+- name: Create clusterssh clusters file
become: true
become_user: domjudge
- lineinfile:
+ template:
+ src: clusters.j2
dest: /home/domjudge/.clusterssh/clusters
- regexp: '^{{ item }}s'
- line: "{{ item }}s {{ groups[item] | join(' ') }}"
- create: true
mode: 0644
- loop:
- - domserver
- - judgehost
- - admin
diff --git a/provision-contest/ansible/roles/clusterssh/templates/clusters.j2 b/provision-contest/ansible/roles/clusterssh/templates/clusters.j2
new file mode 100644
index 00000000..0dfcf597
--- /dev/null
+++ b/provision-contest/ansible/roles/clusterssh/templates/clusters.j2
@@ -0,0 +1,5 @@
+{% for group in groups %}
+{% if groups[group] %}
+{{ group | replace('emergency', 'emergencie') }}{% if group != 'all' %}s{% endif %} {{ groups[group] | join(' ') }}
+{% endif %}
+{% endfor %}
diff --git a/provision-contest/ansible/roles/dj_notify/defaults/main.yml b/provision-contest/ansible/roles/dj_notify/defaults/main.yml
new file mode 100644
index 00000000..1becf1d3
--- /dev/null
+++ b/provision-contest/ansible/roles/dj_notify/defaults/main.yml
@@ -0,0 +1,3 @@
+ALSA_DEVICE: alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp__sink
+NOTIFICATION_SOUND: /usr/share/sounds/sound-icons/trumpet-12.wav
+NOTIFICATION_SOUND_VOLUME: 35536
diff --git a/provision-contest/ansible/roles/dj_notify/files/dj_notify.py b/provision-contest/ansible/roles/dj_notify/files/dj_notify.py
new file mode 100644
index 00000000..74988427
--- /dev/null
+++ b/provision-contest/ansible/roles/dj_notify/files/dj_notify.py
@@ -0,0 +1,87 @@
+from http.server import BaseHTTPRequestHandler, HTTPServer
+import json
+import subprocess
+import gi
+import os
+import webbrowser
+import subprocess
+import traceback
+gi.require_version('Notify', '0.7')
+from gi.repository import Notify
+
+HOSTNAME = "0.0.0.0"
+PORT = 9999
+ALSA_DEVICE = os.environ['ALSA_DEVICE']
+NOTIFICATION_SOUND = os.environ['NOTIFICATION_SOUND']
+NOTIFICATION_SOUND_VOLUME = int(os.environ['NOTIFICATION_SOUND_VOLUME'])
+
+
+def on_notification_closed(notification):
+ print(f"Notification {notification.id} closed.")
+
+
+def on_link_click(notification, action, link):
+ webbrowser.open(link)
+
+
+def filter_notification(title, body, link):
+ return not title.startswith("Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException")
+
+
+class NotifyServer(BaseHTTPRequestHandler):
+ def create_notification(self, title, body, link):
+ notification = Notify.Notification.new(title, body)
+ notification.connect("closed", on_notification_closed)
+ notification.add_action(
+ "action_click",
+ "View in browser",
+ on_link_click,
+ link
+ )
+ notification.show()
+
+
+ def notification_sound(self, sound):
+ # Use Popen to launch a non-blocking background process
+ subprocess.Popen(["paplay", "--volume", str(NOTIFICATION_SOUND_VOLUME), "--device", ALSA_DEVICE, sound])
+
+
+ def do_POST(self):
+ length = int(self.headers.get('Content-Length'))
+ body = self.rfile.read(length)
+ content = json.loads(body)
+ print(json.dumps(content, indent=2))
+
+ att = content['attachments'][0]
+ title = att['title']
+ link = att['title_link']
+ body = att['text']
+
+ if filter_notification(title, body, link):
+ try:
+ self.create_notification(title, body, link)
+ except Exception:
+ print(traceback.format_exc())
+ try:
+ self.notification_sound(NOTIFICATION_SOUND)
+ except Exception:
+ print(traceback.format_exc())
+
+ self.send_response(200)
+ self.send_header("Content-Type", "text/plain")
+ self.end_headers()
+
+ self.wfile.write(bytes("ok", "utf-8"))
+
+
+Notify.init("DOMjudge notifications")
+server = HTTPServer((HOSTNAME, PORT), NotifyServer)
+
+try:
+ server.serve_forever()
+except KeyboardInterrupt:
+ pass
+
+# Clean up
+server.server_close()
+Notify.uninit()
diff --git a/provision-contest/ansible/roles/dj_notify/handlers/main.yml b/provision-contest/ansible/roles/dj_notify/handlers/main.yml
new file mode 100644
index 00000000..b7ce19cb
--- /dev/null
+++ b/provision-contest/ansible/roles/dj_notify/handlers/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Restart dj_notify
+ systemd:
+ name: dj_notify
+ enabled: true
+ state: restarted
+ daemon_reload: true
diff --git a/provision-contest/ansible/roles/dj_notify/tasks/main.yml b/provision-contest/ansible/roles/dj_notify/tasks/main.yml
new file mode 100644
index 00000000..c6391e3d
--- /dev/null
+++ b/provision-contest/ansible/roles/dj_notify/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+# These tasks install the DOMjudge Notify script
+
+- name: Install dj_notify
+ copy:
+ src: "dj_notify.py"
+ dest: "/home/domjudge/bin/dj_notify.py"
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+ notify: Restart dj_notify
+
+- name: Copy dj_notify systemd unit file
+ template:
+ src: "dj_notify.service.j2"
+ dest: "/etc/systemd/system/dj_notify.service"
+ notify: Restart dj_notify
diff --git a/provision-contest/ansible/roles/dj_notify/templates/dj_notify.service.j2 b/provision-contest/ansible/roles/dj_notify/templates/dj_notify.service.j2
new file mode 100644
index 00000000..fb4737e2
--- /dev/null
+++ b/provision-contest/ansible/roles/dj_notify/templates/dj_notify.service.j2
@@ -0,0 +1,21 @@
+[Unit]
+Description="DOMjudge Notify"
+After=network.target
+
+[Service]
+Type=simple
+
+Environment=ALSA_DEVICE={{ ALSA_DEVICE }}
+Environment=NOTIFICATION_SOUND={{ NOTIFICATION_SOUND }}
+Environment=DISPLAY=:0
+Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus
+Environment=PULSE_SERVER=/run/user/1001/pulse/native
+WorkingDirectory=/home/domjudge
+ExecStart=/usr/bin/python3 -u /home/domjudge/bin/dj_notify.py
+User=domjudge
+
+Restart=always
+RestartSec=3
+
+[Install]
+WantedBy=graphical.target
diff --git a/provision-contest/ansible/roles/docker/files/README.md b/provision-contest/ansible/roles/docker/files/README.md
new file mode 100644
index 00000000..2eafe9e8
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/files/README.md
@@ -0,0 +1,5 @@
+# Loading containers from archives
+Any container `.tar` files placed in a `containers-` directory will be loaded as a container for the said host type.
+The container will be tagged with the relative path starting from `containers-`, without the `.tar` file extension.
+For example, the file `containers-glitchtip/glitchtip/glitchtip:v4.1.3.tar` will be loaded as a container tagged `glitchtip/glitchtip:v4.1.3` for the `glitchtip` host type.
+Note that a nested directory structure is needed to tag containers which have both an organization and a container name.
diff --git a/provision-contest/ansible/roles/docker/files/containers-glitchtip/.gitignore b/provision-contest/ansible/roles/docker/files/containers-glitchtip/.gitignore
new file mode 100644
index 00000000..d874ad67
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/files/containers-glitchtip/.gitignore
@@ -0,0 +1 @@
+*.tar
diff --git a/provision-contest/ansible/roles/docker/files/containers-glitchtip/glitchtip/.gitignore b/provision-contest/ansible/roles/docker/files/containers-glitchtip/glitchtip/.gitignore
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/files/containers-glitchtip/glitchtip/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/provision-contest/ansible/roles/docker/files/docker.asc b/provision-contest/ansible/roles/docker/files/docker.asc
new file mode 100644
index 00000000..ee7872e5
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/files/docker.asc
@@ -0,0 +1,62 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth
+lqqwp028AqyY+PRfVMtSYMbjuQuu5byyKR01BbqYhuS3jtqQmljZ/bJvXqnmiVXh
+38UuLa+z077PxyxQhu5BbqntTPQMfiyqEiU+BKbq2WmANUKQf+1AmZY/IruOXbnq
+L4C1+gJ8vfmXQt99npCaxEjaNRVYfOS8QcixNzHUYnb6emjlANyEVlZzeqo7XKl7
+UrwV5inawTSzWNvtjEjj4nJL8NsLwscpLPQUhTQ+7BbQXAwAmeHCUTQIvvWXqw0N
+cmhh4HgeQscQHYgOJjjDVfoY5MucvglbIgCqfzAHW9jxmRL4qbMZj+b1XoePEtht
+ku4bIQN1X5P07fNWzlgaRL5Z4POXDDZTlIQ/El58j9kp4bnWRCJW0lya+f8ocodo
+vZZ+Doi+fy4D5ZGrL4XEcIQP/Lv5uFyf+kQtl/94VFYVJOleAv8W92KdgDkhTcTD
+G7c0tIkVEKNUq48b3aQ64NOZQW7fVjfoKwEZdOqPE72Pa45jrZzvUFxSpdiNk2tZ
+XYukHjlxxEgBdC/J3cMMNRE1F4NCA3ApfV1Y7/hTeOnmDuDYwr9/obA8t016Yljj
+q5rdkywPf4JF8mXUW5eCN1vAFHxeg9ZWemhBtQmGxXnw9M+z6hWwc6ahmwARAQAB
+tCtEb2NrZXIgUmVsZWFzZSAoQ0UgZGViKSA8ZG9ja2VyQGRvY2tlci5jb20+iQI3
+BBMBCgAhBQJYrefAAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEI2BgDwO
+v82IsskP/iQZo68flDQmNvn8X5XTd6RRaUH33kXYXquT6NkHJciS7E2gTJmqvMqd
+tI4mNYHCSEYxI5qrcYV5YqX9P6+Ko+vozo4nseUQLPH/ATQ4qL0Zok+1jkag3Lgk
+jonyUf9bwtWxFp05HC3GMHPhhcUSexCxQLQvnFWXD2sWLKivHp2fT8QbRGeZ+d3m
+6fqcd5Fu7pxsqm0EUDK5NL+nPIgYhN+auTrhgzhK1CShfGccM/wfRlei9Utz6p9P
+XRKIlWnXtT4qNGZNTN0tR+NLG/6Bqd8OYBaFAUcue/w1VW6JQ2VGYZHnZu9S8LMc
+FYBa5Ig9PxwGQOgq6RDKDbV+PqTQT5EFMeR1mrjckk4DQJjbxeMZbiNMG5kGECA8
+g383P3elhn03WGbEEa4MNc3Z4+7c236QI3xWJfNPdUbXRaAwhy/6rTSFbzwKB0Jm
+ebwzQfwjQY6f55MiI/RqDCyuPj3r3jyVRkK86pQKBAJwFHyqj9KaKXMZjfVnowLh
+9svIGfNbGHpucATqREvUHuQbNnqkCx8VVhtYkhDb9fEP2xBu5VvHbR+3nfVhMut5
+G34Ct5RS7Jt6LIfFdtcn8CaSas/l1HbiGeRgc70X/9aYx/V/CEJv0lIe8gP6uDoW
+FPIZ7d6vH+Vro6xuWEGiuMaiznap2KhZmpkgfupyFmplh0s6knymuQINBFit2ioB
+EADneL9S9m4vhU3blaRjVUUyJ7b/qTjcSylvCH5XUE6R2k+ckEZjfAMZPLpO+/tF
+M2JIJMD4SifKuS3xck9KtZGCufGmcwiLQRzeHF7vJUKrLD5RTkNi23ydvWZgPjtx
+Q+DTT1Zcn7BrQFY6FgnRoUVIxwtdw1bMY/89rsFgS5wwuMESd3Q2RYgb7EOFOpnu
+w6da7WakWf4IhnF5nsNYGDVaIHzpiqCl+uTbf1epCjrOlIzkZ3Z3Yk5CM/TiFzPk
+z2lLz89cpD8U+NtCsfagWWfjd2U3jDapgH+7nQnCEWpROtzaKHG6lA3pXdix5zG8
+eRc6/0IbUSWvfjKxLLPfNeCS2pCL3IeEI5nothEEYdQH6szpLog79xB9dVnJyKJb
+VfxXnseoYqVrRz2VVbUI5Blwm6B40E3eGVfUQWiux54DspyVMMk41Mx7QJ3iynIa
+1N4ZAqVMAEruyXTRTxc9XW0tYhDMA/1GYvz0EmFpm8LzTHA6sFVtPm/ZlNCX6P1X
+zJwrv7DSQKD6GGlBQUX+OeEJ8tTkkf8QTJSPUdh8P8YxDFS5EOGAvhhpMBYD42kQ
+pqXjEC+XcycTvGI7impgv9PDY1RCC1zkBjKPa120rNhv/hkVk/YhuGoajoHyy4h7
+ZQopdcMtpN2dgmhEegny9JCSwxfQmQ0zK0g7m6SHiKMwjwARAQABiQQ+BBgBCAAJ
+BQJYrdoqAhsCAikJEI2BgDwOv82IwV0gBBkBCAAGBQJYrdoqAAoJEH6gqcPyc/zY
+1WAP/2wJ+R0gE6qsce3rjaIz58PJmc8goKrir5hnElWhPgbq7cYIsW5qiFyLhkdp
+YcMmhD9mRiPpQn6Ya2w3e3B8zfIVKipbMBnke/ytZ9M7qHmDCcjoiSmwEXN3wKYI
+mD9VHONsl/CG1rU9Isw1jtB5g1YxuBA7M/m36XN6x2u+NtNMDB9P56yc4gfsZVES
+KA9v+yY2/l45L8d/WUkUi0YXomn6hyBGI7JrBLq0CX37GEYP6O9rrKipfz73XfO7
+JIGzOKZlljb/D9RX/g7nRbCn+3EtH7xnk+TK/50euEKw8SMUg147sJTcpQmv6UzZ
+cM4JgL0HbHVCojV4C/plELwMddALOFeYQzTif6sMRPf+3DSj8frbInjChC3yOLy0
+6br92KFom17EIj2CAcoeq7UPhi2oouYBwPxh5ytdehJkoo+sN7RIWua6P2WSmon5
+U888cSylXC0+ADFdgLX9K2zrDVYUG1vo8CX0vzxFBaHwN6Px26fhIT1/hYUHQR1z
+VfNDcyQmXqkOnZvvoMfz/Q0s9BhFJ/zU6AgQbIZE/hm1spsfgvtsD1frZfygXJ9f
+irP+MSAI80xHSf91qSRZOj4Pl3ZJNbq4yYxv0b1pkMqeGdjdCYhLU+LZ4wbQmpCk
+SVe2prlLureigXtmZfkqevRz7FrIZiu9ky8wnCAPwC7/zmS18rgP/17bOtL4/iIz
+QhxAAoAMWVrGyJivSkjhSGx1uCojsWfsTAm11P7jsruIL61ZzMUVE2aM3Pmj5G+W
+9AcZ58Em+1WsVnAXdUR//bMmhyr8wL/G1YO1V3JEJTRdxsSxdYa4deGBBY/Adpsw
+24jxhOJR+lsJpqIUeb999+R8euDhRHG9eFO7DRu6weatUJ6suupoDTRWtr/4yGqe
+dKxV3qQhNLSnaAzqW/1nA3iUB4k7kCaKZxhdhDbClf9P37qaRW467BLCVO/coL3y
+Vm50dwdrNtKpMBh3ZpbB1uJvgi9mXtyBOMJ3v8RZeDzFiG8HdCtg9RvIt/AIFoHR
+H3S+U79NT6i0KPzLImDfs8T7RlpyuMc4Ufs8ggyg9v3Ae6cN3eQyxcK3w0cbBwsh
+/nQNfsA6uu+9H7NhbehBMhYnpNZyrHzCmzyXkauwRAqoCbGCNykTRwsur9gS41TQ
+M8ssD1jFheOJf3hODnkKU+HKjvMROl1DK7zdmLdNzA1cvtZH/nCC9KPj1z8QC47S
+xx+dTZSx4ONAhwbS/LN3PoKtn8LPjY9NP9uDWI+TWYquS2U+KHDrBDlsgozDbs/O
+jCxcpDzNmXpWQHEtHU7649OXHP7UeNST1mCUCH5qdank0V1iejF6/CfTFU4MfcrG
+YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
+=0YYh
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/provision-contest/ansible/roles/docker/tasks/load-container.yml b/provision-contest/ansible/roles/docker/tasks/load-container.yml
new file mode 100644
index 00000000..1f4c6df2
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/tasks/load-container.yml
@@ -0,0 +1,35 @@
+---
+- name: Load the container from archive if needed
+ block:
+ - name: Check for existing container
+ community.docker.docker_image_info:
+ name: "{{ img_name }}"
+ register: result
+
+ - name: Transfer and load the container
+ block:
+ - name: Create temp container directory
+ file:
+ path: /tmp/dj_ansible
+ state: directory
+ owner: root
+ group: root
+ mode: 0700
+
+ - name: Transfer container archive
+ copy:
+ src: "{{ item.src }}"
+ dest: "{{ img_path }}"
+ owner: root
+ group: root
+ mode: 0700
+
+ - name: Import container from archive
+ community.docker.docker_image:
+ name: "{{ img_name }}"
+ load_path: "{{ img_path }}"
+ source: load
+ when: not result.images
+ vars:
+ img_name: "{{ item.path | splitext | first }}"
+ img_path: "/tmp/dj_ansible/{{ item.path | basename }}"
diff --git a/provision-contest/ansible/roles/docker/tasks/main.yml b/provision-contest/ansible/roles/docker/tasks/main.yml
new file mode 100644
index 00000000..537c7304
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/tasks/main.yml
@@ -0,0 +1,39 @@
+---
+- name: Add Docker GPG apt Key
+ apt_key:
+ data: "{{ lookup('ansible.builtin.file', 'docker.asc') }}"
+ state: present
+
+- name: Add Docker Repository
+ when: not WF_RESTRICTED_NETWORK
+ apt_repository:
+ repo: deb https://download.docker.com/linux/ubuntu jammy stable
+ state: present
+
+- name: Install Docker and dependencies
+ apt:
+ state: present
+ install_recommends: false
+ pkg:
+ - containerd.io
+ - docker-buildx-plugin
+ - docker-ce
+ - docker-ce-cli
+ - docker-compose-plugin
+ - python3-pip
+ - python3-docker
+
+# The `runc` script interferes with running Docker containers.
+# Mark it as not executable such that containers can run normally.
+# If the machine does need the `runc` in the image, a custom fix has to be applied.
+- name: Remove executable bit from "/usr/local/bin/runc"
+ when: ICPC_IMAGE
+ file:
+ dest: /usr/local/bin/runc
+ mode: -x
+
+- name: Load container archives
+ include_tasks: load-container.yml
+ with_filetree:
+ - files/containers-{{ host_type }}/
+ when: item.state == 'file' and (item.path | splitext | last) == ".tar"
diff --git a/provision-contest/ansible/roles/docker/vars/.gitignore b/provision-contest/ansible/roles/docker/vars/.gitignore
new file mode 100644
index 00000000..1cda54be
--- /dev/null
+++ b/provision-contest/ansible/roles/docker/vars/.gitignore
@@ -0,0 +1 @@
+*.yml
diff --git a/provision-contest/ansible/roles/domjudge_build/handlers/main.yml b/provision-contest/ansible/roles/domjudge_build/handlers/main.yml
index 8e0de116..b352492f 100644
--- a/provision-contest/ansible/roles/domjudge_build/handlers/main.yml
+++ b/provision-contest/ansible/roles/domjudge_build/handlers/main.yml
@@ -1,8 +1,14 @@
---
-# Define here handlers associated to this role.
-
-- name: fix permissions on domjudge inplace-install
+- name: Fix permissions on domjudge inplace-install
command: make -C {{ DJ_DIR }} inplace-postinstall-permissions
-- name: restart rsyslog
- service: name=rsyslog enabled=true state=restarted
+- name: Fix composer autoload
+ command:
+ cmd: composer dump-autoload -o
+ chdir: "{{ DJ_DIR }}/webapp"
+
+- name: Restart rsyslog
+ service:
+ name: rsyslog
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/domjudge_build/meta/main.yml b/provision-contest/ansible/roles/domjudge_build/meta/main.yml
index 551c866e..10cf7c8e 100644
--- a/provision-contest/ansible/roles/domjudge_build/meta/main.yml
+++ b/provision-contest/ansible/roles/domjudge_build/meta/main.yml
@@ -2,4 +2,6 @@
---
dependencies:
- role: domjudge_user
+ tags: domjudge_user
- role: domjudge_checkout
+ tags: domjudge_checkout
diff --git a/provision-contest/ansible/roles/domjudge_build/tasks/main.yml b/provision-contest/ansible/roles/domjudge_build/tasks/main.yml
index 5737e455..4add2702 100644
--- a/provision-contest/ansible/roles/domjudge_build/tasks/main.yml
+++ b/provision-contest/ansible/roles/domjudge_build/tasks/main.yml
@@ -1,7 +1,7 @@
---
# Tasks to build DOMjudge
-- name: add the restapi secret file
+- name: Add the restapi secret file
template:
src: restapi.secret.j2
dest: "{{ DJ_DIR }}/etc/restapi.secret"
@@ -9,7 +9,7 @@
group: domjudge
mode: 0600
-- name: run inplace-conf
+- name: Run inplace-conf
become: true
become_user: domjudge
command: make inplace-conf CONFIGURE_FLAGS='--disable-doc-build --with-baseurl={{ DOMSERVER }}/'
@@ -18,20 +18,23 @@
chdir: "{{ DJ_DIR }}"
creates: "{{ DJ_DIR }}/paths.mk"
-- name: check if domjudge is built
+- name: Check if domjudge is built
stat: path="{{ DJ_DIR }}/judge/judgedaemon"
register: judgedaemon_binary
+ check_mode: no
-- name: build domjudge
+- name: Build domjudge
become: true
become_user: domjudge
command: make inplace-install
args:
chdir: "{{ DJ_DIR }}"
- notify: fix permissions on domjudge inplace-install
+ notify:
+ - Fix permissions on domjudge inplace-install
+ - Fix composer autoload
when: (git_working_copy is defined and git_working_copy.changed) or dj_configured.changed or not judgedaemon_binary.stat.exists
-- name: copy domjudge-sudoers file
+- name: Copy domjudge-sudoers file
copy:
remote_src: true
src: "{{ DJ_DIR }}/etc/sudoers-domjudge"
@@ -40,16 +43,16 @@
owner: root
group: root
-- name: configure domjudge logging
+- name: Configure domjudge logging
copy:
src: rsyslog.domjudge.conf
dest: /etc/rsyslog.d/domjudge.conf
mode: 0644
group: root
owner: root
- notify: restart rsyslog
+ notify: Restart rsyslog
-- name: configure domjudge logrotate
+- name: Configure domjudge logrotate
copy:
src: logrotate.domjudge
dest: /etc/logrotate.d/domjudge
diff --git a/provision-contest/ansible/roles/domjudge_build/templates/restapi.secret.j2 b/provision-contest/ansible/roles/domjudge_build/templates/restapi.secret.j2
index b5adfce3..b086f548 100644
--- a/provision-contest/ansible/roles/domjudge_build/templates/restapi.secret.j2
+++ b/provision-contest/ansible/roles/domjudge_build/templates/restapi.secret.j2
@@ -1,3 +1,3 @@
# Randomly generated on host domjudge-judgehost3, Thu Apr 6 23:36:19 MDT 2017
# Format: ' '
-default {{DOMSERVER_URL}}/api {{API_USER}} {{API_PASSWORD}}
+default {{DOMSERVER_URL}}/api {{JUDGEHOST_USER}} {{JUDGEHOST_PASSWORD}}
diff --git a/provision-contest/ansible/roles/domjudge_checkout/files/.gitignore b/provision-contest/ansible/roles/domjudge_checkout/files/.gitignore
index 502167fa..13ecffc5 100644
--- a/provision-contest/ansible/roles/domjudge_checkout/files/.gitignore
+++ b/provision-contest/ansible/roles/domjudge_checkout/files/.gitignore
@@ -1 +1,3 @@
/lib
+/lib-vendor.tgz
+/vendor.tgz
diff --git a/provision-contest/ansible/roles/domjudge_checkout/files/lib-vendor.tgz b/provision-contest/ansible/roles/domjudge_checkout/files/lib-vendor.tgz
deleted file mode 100644
index 21e31971..00000000
Binary files a/provision-contest/ansible/roles/domjudge_checkout/files/lib-vendor.tgz and /dev/null differ
diff --git a/provision-contest/ansible/roles/domjudge_checkout/meta/main.yml b/provision-contest/ansible/roles/domjudge_checkout/meta/main.yml
index 4a4f743a..a480e52b 100644
--- a/provision-contest/ansible/roles/domjudge_checkout/meta/main.yml
+++ b/provision-contest/ansible/roles/domjudge_checkout/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: domjudge_user
+ tags: domjudge_user
diff --git a/provision-contest/ansible/roles/domjudge_checkout/tasks/main.yml b/provision-contest/ansible/roles/domjudge_checkout/tasks/main.yml
index 3fdd9aa7..96a7b68a 100644
--- a/provision-contest/ansible/roles/domjudge_checkout/tasks/main.yml
+++ b/provision-contest/ansible/roles/domjudge_checkout/tasks/main.yml
@@ -1,7 +1,7 @@
---
# These tasks create a checkout of the DOMjudge repo
-- name: create working copy directory
+- name: Create working copy directory
file:
path: "{{ DJ_DIR }}"
state: directory
@@ -9,6 +9,14 @@
group: domjudge
mode: 0755
+- name: Create ansible tmp directory
+ file:
+ path: /home/domjudge/.ansible/tmp
+ state: directory
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+
- name: Update repo URL based on network
set_fact:
dj_git_repo: "{{ DJ_GIT_REPO_RESTRICTED if WF_RESTRICTED_NETWORK else DJ_GIT_REPO }}"
@@ -16,27 +24,37 @@
- name: Create working copy of the domjudge repo
become: true
become_user: domjudge
- git: repo={{ dj_git_repo }} dest={{ DJ_DIR }} version={{ DJ_BRANCH }} accept_hostkey=yes update=yes
+ git:
+ repo: "{{ dj_git_repo }}"
+ dest: "{{ DJ_DIR }}"
+ version: "{{ DJ_BRANCH }}"
+ accept_hostkey: true
+ update: true
+ force: "{{ DJ_FORCE_UPDATE }}"
register: git_working_copy
-- name: Check composer dependencies present
- become: false
- delegate_to: localhost
- stat:
- path: files/lib/vendor
- register: libvendor
+- name: Install composer dependencies
+ when: WF_RESTRICTED_NETWORK
+ block:
+ - name: Check composer dependencies present
+ become: false
+ delegate_to: localhost
+ stat:
+ path: roles/domjudge_checkout/files/webapp/vendor
+ register: vendor
+ check_mode: no
-- name: Copy in composer dependencies (if they exist locally)
- synchronize:
- src: files/lib/vendor/
- dest: "{{ DJ_DIR }}/lib/vendor/"
- owner: false
- use_ssh_args: true
- when: libvendor.stat.exists
+ - name: Copy in composer dependencies (if they exist locally)
+ synchronize:
+ src: files/webapp/vendor/
+ dest: "{{ DJ_DIR }}/webapp/vendor/"
+ owner: false
+ use_ssh_args: true
+ when: vendor.stat.exists
-- name: fix ownership of lib/vendor
- file:
- path: "{{ DJ_DIR }}/lib/vendor"
- recurse: true
- owner: domjudge
- group: domjudge
+ - name: Fix ownership of webapp/vendor
+ file:
+ path: "{{ DJ_DIR }}/webapp/vendor"
+ recurse: true
+ owner: domjudge
+ group: domjudge
diff --git a/provision-contest/ansible/roles/domjudge_user/handlers/main.yml b/provision-contest/ansible/roles/domjudge_user/handlers/main.yml
index e905aa84..d1c7e282 100644
--- a/provision-contest/ansible/roles/domjudge_user/handlers/main.yml
+++ b/provision-contest/ansible/roles/domjudge_user/handlers/main.yml
@@ -1,5 +1,6 @@
---
-# Define here handlers associated to this role.
-
-- name: restart gdm
- service: name=gdm3 enabled=true state=restarted
+- name: Restart gdm
+ service:
+ name: gdm3
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/domjudge_user/tasks/gitconfig.yml b/provision-contest/ansible/roles/domjudge_user/tasks/gitconfig.yml
new file mode 100644
index 00000000..3bb97da2
--- /dev/null
+++ b/provision-contest/ansible/roles/domjudge_user/tasks/gitconfig.yml
@@ -0,0 +1,22 @@
+- name: "Configure git user {{ user_item.login }}"
+ ini_file:
+ dest: "{{ user_item.homedir }}/.gitconfig"
+ section: user
+ option: "{{ item.name }}"
+ value: "{{ item.value }}"
+ owner: "{{ user_item.login }}"
+ group: "{{ user_item.login }}"
+ mode: 0644
+ loop:
+ - { name: 'email', value: 'team@domjudge.org' }
+ - { name: 'name', value: 'DOMjudge team' }
+
+- name: "Configure git to rebase on pull for {{ user_item.login }}"
+ ini_file:
+ dest: "{{ user_item.homedir }}/.gitconfig"
+ section: branch
+ option: "autosetuprebase"
+ value: "remote"
+ owner: "{{ user_item.login }}"
+ group: "{{ user_item.login }}"
+ mode: 0644
diff --git a/provision-contest/ansible/roles/domjudge_user/tasks/main.yml b/provision-contest/ansible/roles/domjudge_user/tasks/main.yml
index d9f7efc4..53c86d76 100644
--- a/provision-contest/ansible/roles/domjudge_user/tasks/main.yml
+++ b/provision-contest/ansible/roles/domjudge_user/tasks/main.yml
@@ -1,7 +1,7 @@
---
# These tasks create a domjudge system account and fix sudo/autologin
-- name: create domjudge user
+- name: Create domjudge user
user:
name: domjudge
shell: /bin/bash
@@ -15,49 +15,36 @@
regexp: '^%sudo'
line: '%sudo ALL=(ALL) NOPASSWD: ALL'
-- name: configure git user
- ini_file:
- dest: /home/domjudge/.gitconfig
- section: user
- option: "{{ item.name }}"
- value: "{{ item.value }}"
- owner: domjudge
- group: domjudge
- mode: 0644
+- include_tasks: gitconfig.yml
loop:
- - { name: 'email', value: 'team@domjudge.org' }
- - { name: 'name', value: 'DOMjudge team' }
+ - { login: 'domjudge', homedir: '/home/domjudge' }
+ - { login: 'root', homedir: '/root' }
+ loop_control:
+ loop_var: user_item
-- name: configure git to rebase on pull
- ini_file:
- dest: /home/domjudge/.gitconfig
- section: branch
- option: "autosetuprebase"
- value: "remote"
- owner: domjudge
- group: domjudge
- mode: 0644
+- name: Setup autologin on desktop installs
+ block:
+ - name: Enable GDM autologin
+ lineinfile:
+ path: /etc/gdm3/custom.conf
+ regexp: 'AutomaticLoginEnable'
+ line: 'AutomaticLoginEnable=true'
+ create: true
+ mode: 0644
+ notify: Restart gdm
-- name: enable GDM autologin
- lineinfile:
- path: /etc/gdm3/custom.conf
- regexp: 'AutomaticLoginEnable'
- line: 'AutomaticLoginEnable=true'
- create: true
- mode: 0644
- notify: restart gdm
-
-- name: Automatically login domjudge user
- lineinfile:
- path: /etc/gdm3/custom.conf
- regexp: 'AutomaticLogin='
- line: 'AutomaticLogin=domjudge'
- notify: restart gdm
+ - name: Automatically login domjudge user
+ lineinfile:
+ path: /etc/gdm3/custom.conf
+ regexp: 'AutomaticLogin\s*='
+ line: 'AutomaticLogin=domjudge'
+ notify: Restart gdm
-- name: make sure autostart directory exists
- file:
- dest: /home/domjudge/.config/autostart
- state: directory
- owner: domjudge
- group: domjudge
- mode: 0755
+ - name: Make sure autostart directory exists
+ file:
+ dest: /home/domjudge/.config/autostart
+ state: directory
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+ when: GRAPHICAL
diff --git a/provision-contest/ansible/roles/domlogo/files/README.md b/provision-contest/ansible/roles/domlogo/files/README.md
new file mode 100644
index 00000000..2d1d2ac3
--- /dev/null
+++ b/provision-contest/ansible/roles/domlogo/files/README.md
@@ -0,0 +1,17 @@
+# Preparing DOMlogo
+
+First, create the following files:
+- `images/logos/DOMjudge.png`, a 64x64 DOMjudge logo with transparent background.
+- `images/photos/crew.png`, an image with a width of 1024 (and any height) to show for teams without a photo.
+- `images/photos/idle.png`, an image with a width of 1024 (and any height) to show when the judgedaemon is idle.
+
+Next, add the needed Python dependencies to the `lib` folder, within a folder called `python3.10`. You can copy this
+folder from a local machine and it should contain the `FreeSimpleGUI` and `requests` Python packages.
+
+Optionally you can create a file `images/config.yaml` with something like:
+
+```yaml
+host-bg-color: '#013370'
+```
+
+DOMlogo will use the DOMjudge API to download logos and photos for all teams, so no further configuration should be needed.
diff --git a/provision-contest/ansible/roles/domlogo/files/lib/.gitignore b/provision-contest/ansible/roles/domlogo/files/lib/.gitignore
index b960de0d..3c8ed4fa 100644
--- a/provision-contest/ansible/roles/domlogo/files/lib/.gitignore
+++ b/provision-contest/ansible/roles/domlogo/files/lib/.gitignore
@@ -1 +1 @@
-python3.8
+python3.*
diff --git a/provision-contest/ansible/roles/domlogo/tasks/main.yml b/provision-contest/ansible/roles/domlogo/tasks/main.yml
index 7bd14cf8..f5811239 100644
--- a/provision-contest/ansible/roles/domlogo/tasks/main.yml
+++ b/provision-contest/ansible/roles/domlogo/tasks/main.yml
@@ -1,13 +1,14 @@
---
# These tasks install domlogo
-- name: install python3 modules for domlogo
+- name: Install python3 modules and imagemagick for domlogo
apt:
state: present
pkg:
- python3-tk
+ - imagemagick
-- name: install domlogo
+- name: Install domlogo
copy:
src: domlogo.py
dest: "{{ DJ_DIR }}"
@@ -15,24 +16,41 @@
group: domjudge
mode: 0755
-- name: install domlogo env
+- name: Install domlogo Python libraries
synchronize:
src: lib
dest: /home/domjudge/.local/
owner: false
use_ssh_args: true
-- name: install domlogo images
+- name: Fix ownership of domlogo env
+ file:
+ path: "/home/domjudge/.local/lib/python3.10"
+ recurse: true
+ owner: domjudge
+ group: domjudge
+
+- name: Install domlogo images
synchronize:
- src: images
- dest: "{{ DJ_DIR }}"
+ src: images/
+ dest: "{{ DJ_DIR }}/domlogo-files/"
owner: false
use_ssh_args: true
-- name: add autostart shortcuts
- template:
- src: domjudgelogo.desktop.j2
- dest: /home/domjudge/.config/autostart/domjudgelogo.desktop
+- name: Fix ownership of domlogo images
+ file:
+ path: "{{ DJ_DIR }}/domlogo-files"
+ recurse: true
owner: domjudge
group: domjudge
- mode: 0755
+
+- name: Copy domlogo systemd unit file
+ template:
+ src: "domlogo.service.j2"
+ dest: "/etc/systemd/system/domlogo.service"
+
+- name: Enable and start domlogo
+ systemd:
+ name: domlogo
+ enabled: true
+ state: started
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2 b/provision-contest/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2
deleted file mode 100644
index 7cb14e98..00000000
--- a/provision-contest/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2
+++ /dev/null
@@ -1,4 +0,0 @@
-[Desktop Entry]
-Name=domjudgelogo
-Type=Application
-Exec=bash -c "sleep 10 && cd {{ DJ_DIR }} && python3 ./domlogo.py 1>/home/domjudge/logo.out 2>/home/domjudge/logo.err"
diff --git a/provision-contest/ansible/roles/domlogo/templates/domlogo.service.j2 b/provision-contest/ansible/roles/domlogo/templates/domlogo.service.j2
new file mode 100644
index 00000000..71c899e3
--- /dev/null
+++ b/provision-contest/ansible/roles/domlogo/templates/domlogo.service.j2
@@ -0,0 +1,18 @@
+[Unit]
+Description="DOMlogo"
+After=network.target
+
+[Service]
+Type=simple
+
+Environment=REQUESTS_CA_BUNDLE=/usr/local/share/ca-certificates/domserver.crt
+Environment=DISPLAY=:0
+WorkingDirectory={{ DJ_DIR }}
+ExecStart=domlogo.py
+User=domjudge
+
+Restart=always
+RestartSec=3
+
+[Install]
+WantedBy=graphical.target
diff --git a/provision-contest/ansible/roles/domserver/defaults/main.yml b/provision-contest/ansible/roles/domserver/defaults/main.yml
new file mode 100644
index 00000000..e2a3a2cf
--- /dev/null
+++ b/provision-contest/ansible/roles/domserver/defaults/main.yml
@@ -0,0 +1,6 @@
+DOMSERVER_PACKAGES:
+ - nginx
+ - php-fpm
+ - php-intl
+ - texlive-latex-recommended
+ - texlive-latex-extra
diff --git a/provision-contest/ansible/roles/domserver/handlers/main.yml b/provision-contest/ansible/roles/domserver/handlers/main.yml
index 6fe95874..fae61e35 100644
--- a/provision-contest/ansible/roles/domserver/handlers/main.yml
+++ b/provision-contest/ansible/roles/domserver/handlers/main.yml
@@ -1,8 +1,6 @@
---
-# Define here handlers associated to this role.
-
-- name: restart nginx
- service: name=nginx enabled=true state=restarted
-
-- name: restart PHP FPM
- service: name=php7.4-fpm enabled=true state=restarted
+- name: Restart PHP FPM
+ service:
+ name: php{{ php_version.stdout }}-fpm
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/domserver/meta/main.yml b/provision-contest/ansible/roles/domserver/meta/main.yml
index 8daa8904..f073195b 100644
--- a/provision-contest/ansible/roles/domserver/meta/main.yml
+++ b/provision-contest/ansible/roles/domserver/meta/main.yml
@@ -2,4 +2,6 @@
---
dependencies:
- role: mysql_server
+ tags: mysql_server
- role: domjudge_build
+ tags: domjudge_build
diff --git a/provision-contest/ansible/roles/domserver/tasks/main.yml b/provision-contest/ansible/roles/domserver/tasks/main.yml
index db44a25e..28bb5df3 100644
--- a/provision-contest/ansible/roles/domserver/tasks/main.yml
+++ b/provision-contest/ansible/roles/domserver/tasks/main.yml
@@ -1,100 +1,98 @@
---
# These tasks configure DOMjudge as domserver
-- name: install dbpasswords.secret file
+- name: Install dbpasswords.secret file
template:
src: dbpasswords.secret.j2
dest: "{{ DJ_DIR }}/etc/dbpasswords.secret"
mode: 0644
group: root
owner: root
- notify: fix permissions on domjudge inplace-install
+ notify: Fix permissions on domjudge inplace-install
-- name: install initial_admin_password.secret file
+- name: Install initial_admin_password.secret file
template:
src: initial_admin_password.secret.j2
dest: "{{ DJ_DIR }}/etc/initial_admin_password.secret"
mode: 0644
group: domjudge
owner: domjudge
- notify: fix permissions on domjudge inplace-install
+ notify: Fix permissions on domjudge inplace-install
# When using replication, the DB will be dropped and recreated on the slave later.
-- name: check if the database is configured
- command: "{{ DJ_DIR }}/bin/dj_setup_database -u root status"
+- name: Check if the database is configured
+ command: "{{ DJ_DIR }}/bin/dj_setup_database -s -u root status"
register: db_status
ignore_errors: true
changed_when: false
+ check_mode: no
-- name: make sure the database is configured
- command: "{{ DJ_DIR }}/bin/dj_setup_database -u root bare-install"
+- name: Make sure the database is configured
+ command: "{{ DJ_DIR }}/bin/dj_setup_database -s -u root bare-install"
when: "'failed' in db_status.stdout"
-- name: install required packages
+- name: Install required packages
apt:
state: present
- pkg:
- - nginx
- - php-fpm
- - php-intl
+ pkg: "{{ DOMSERVER_PACKAGES }}"
-- name: set PHP timezone for FPM
+- name: Set PHP timezone for FPM
lineinfile:
- dest: /etc/php/7.4/fpm/php.ini
+ dest: "/etc/php/{{ php_version.stdout }}/fpm/php.ini"
state: present
regexp: 'date\.timezone\s*='
line: 'date.timezone = {{ TIMEZONE }}'
+ notify: Restart PHP FPM
-- name: enable PHP modules
+- name: Enable PHP modules
command: phpenmod {{ item }}
args:
- creates: /etc/php/7.4/fpm/conf.d/20-{{ item }}.ini
- loop:
- - zip
- - intl
+ creates: "/etc/php/{{ php_version.stdout }}/fpm/conf.d/20-{{ item }}.ini"
+ loop: "{{ DOMSERVER_PACKAGES | map('regex_search', 'php-') | select() }}"
+ notify: Restart PHP FPM
-- name: add domjudge nginx conf
+- name: Add domjudge nginx conf
template:
src: nginx-domjudge.conf.j2
dest: /etc/nginx/sites-available/domjudge.conf
mode: 0644
group: root
owner: root
- notify: restart nginx
+ notify: Restart nginx
-- name: add domjudge inner nginx conf
+- name: Add domjudge inner nginx conf
template:
src: nginx-domjudge-inner.j2
dest: /etc/nginx/snippets/domjudge-inner
mode: 0644
group: root
owner: root
- notify: restart nginx
+ notify: Restart nginx
-- name: enable nginx conf for domjudge
+- name: Enable nginx conf for domjudge
file:
src: /etc/nginx/sites-available/domjudge.conf
dest: /etc/nginx/sites-enabled/domjudge.conf
state: link
- notify: restart nginx
+ notify: Restart nginx
-- name: disable default nginx site
+- name: Disable default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
- notify: restart nginx
+ notify: Restart nginx
-- name: symlink domjudge FPM conf
+- name: Symlink domjudge FPM conf
file:
- path: /etc/php/7.4/fpm/pool.d/domjudge.conf
+ path: "/etc/php/{{ php_version.stdout }}/fpm/pool.d/domjudge.conf"
src: "{{ DJ_DIR }}/etc/domjudge-fpm.conf"
state: link
force: true
- notify: restart PHP FPM
+ notify: Restart PHP FPM
-- name: set PHP settings
+- name: Set PHP settings
lineinfile:
- dest: /etc/php/7.4/fpm/pool.d/domjudge.conf
+ dest: "/etc/php/{{ php_version.stdout }}/fpm/pool.d/domjudge.conf"
regexp: "{{ item.regexp }}"
line: "{{ item.key }} = {{ item.value }}"
loop:
@@ -104,4 +102,24 @@
- { key: 'php_admin_value[upload_max_filesize]', regexp: '^php_admin_value\[upload_max_filesize\]', value: '{{ PHP_UPLOAD_MAX_FILESIZE }}' }
- { key: 'php_admin_value[post_max_size]', regexp: '^php_admin_value\[post_max_size\]', value: '{{ PHP_POST_MAX_SIZE }}' }
- { key: 'php_admin_value[max_file_uploads]', regexp: '^php_admin_value\[max_file_uploads\]', value: '{{ PHP_MAX_FILE_UPLOADS }}' }
- notify: restart PHP FPM
+ notify: Restart PHP FPM
+
+- name: Start domserver webservices
+ systemd:
+ name: "{{ item }}"
+ enabled: true
+ state: started
+ loop:
+ - nginx
+ - php{{ php_version.stdout }}-fpm
+
+- name: Send errors to Sentry
+ lineinfile:
+ regexp: '^SENTRY_DSN='
+ state: present
+ line: "SENTRY_DSN={{ SENTRY_DSN | default('') }}"
+ dest: "{{ DJ_DIR }}/webapp/.env.local"
+ create: true
+ mode: 0664
+ group: domjudge
+ owner: domjudge
diff --git a/provision-contest/ansible/roles/domserver/templates/nginx-domjudge-inner.j2 b/provision-contest/ansible/roles/domserver/templates/nginx-domjudge-inner.j2
index 2d19afe6..cf411ae1 100644
--- a/provision-contest/ansible/roles/domserver/templates/nginx-domjudge-inner.j2
+++ b/provision-contest/ansible/roles/domserver/templates/nginx-domjudge-inner.j2
@@ -13,9 +13,33 @@ set $prefix '';
location / {
root $domjudgeRoot;
try_files $uri @domjudgeFront;
+
+ # Handle API requests separately to be able to split the log
+ location /api/ {
+ try_files $uri @domjudgeFrontApi;
+ error_log /var/log/nginx/domjudge-api.log;
+ access_log /var/log/nginx/domjudge-api.log dj_access;
+ }
}
location @domjudgeFront {
+ fastcgi_split_path_info ^(.+\.php)(/.*)$;
+ fastcgi_pass domjudge;
+ include fastcgi_params;
+ fastcgi_param SERVER_NAME $host;
+ fastcgi_read_timeout 600;
+ fastcgi_send_timeout 600;
+
+ fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
+ fastcgi_param SCRIPT_NAME $prefix/index.php;
+ fastcgi_param REQUEST_URI $prefix$uri?$args;
+ fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
+ # Prevents URIs that include the front controller. This will 404:
+ # http://domain.tld/app_dev.php/some-path
+ internal;
+}
+
+location @domjudgeFrontApi {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass domjudge;
include fastcgi_params;
@@ -24,9 +48,14 @@ location @domjudgeFront {
fastcgi_param SCRIPT_NAME $prefix/index.php;
fastcgi_param REQUEST_URI $prefix$uri?$args;
fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
+ fastcgi_param HTTPS $http_x_forwarded_proto if_not_empty;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/app_dev.php/some-path
internal;
+
+ # Use a separate log file for the API
+ error_log /var/log/nginx/domjudge-api.log;
+ access_log /var/log/nginx/domjudge-api.log dj_access;
}
# The X-Frame-Options header defends against so-called 'clickjacking' attacks.
@@ -38,6 +67,12 @@ add_header X-Frame-Options "DENY";
add_header Referrer-Policy "same-origin";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
+add_header X-Robots-Tag "none" always;
+
+{% if FRONT_REVERSE_PROXY is defined %}
+set_real_ip_from {{ FRONT_REVERSE_PROXY }};
+real_ip_header X-Forwarded-For;
+{% endif %}
error_log /var/log/nginx/domjudge.log;
-access_log /var/log/nginx/domjudge.log;
+access_log /var/log/nginx/domjudge.log dj_access;
diff --git a/provision-contest/ansible/roles/domserver/templates/nginx-domjudge.conf.j2 b/provision-contest/ansible/roles/domserver/templates/nginx-domjudge.conf.j2
index b80a9bd0..9fc5e3de 100644
--- a/provision-contest/ansible/roles/domserver/templates/nginx-domjudge.conf.j2
+++ b/provision-contest/ansible/roles/domserver/templates/nginx-domjudge.conf.j2
@@ -7,16 +7,17 @@ upstream domjudge {
server unix:/var/run/php-fpm-domjudge.sock; # if using with etc/domjudge-fpm.conf
}
+# Custom log format that adds the request processing time.
+log_format dj_access '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent $request_time "$http_referer" "$http_user_agent"';
+
server {
- listen 80;
- listen [::]:80;
+ listen 80 default;
server_name _default_;
return 308 https://$host$request_uri; # enforce https
}
server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
+ listen 443 ssl http2 default;
ssl_certificate {{DOMSERVER_SSL_CERT}};
ssl_certificate_key {{DOMSERVER_SSL_KEY}};
@@ -25,5 +26,6 @@ server {
add_header Strict-Transport-Security max-age=31556952;
+ send_timeout 36000s;
include /etc/nginx/snippets/domjudge-inner;
}
diff --git a/provision-contest/ansible/roles/glitchtip/defaults/main.yml b/provision-contest/ansible/roles/glitchtip/defaults/main.yml
new file mode 100644
index 00000000..df4a7c22
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/defaults/main.yml
@@ -0,0 +1,3 @@
+GLITCHTIP_PORT: 8000
+GLITCHTIP_TOKEN:
+GLITCHTIP_WEBHOOK_DOMAIN: domjudge-ccsadmin2
diff --git a/provision-contest/ansible/roles/glitchtip/handlers/main.yml b/provision-contest/ansible/roles/glitchtip/handlers/main.yml
new file mode 100644
index 00000000..61571dfd
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/handlers/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Restart GlitchTip
+ community.docker.docker_compose_v2:
+ project_src: /opt/glitchtip
+ files:
+ - docker-compose.yaml
+ state: restarted
diff --git a/provision-contest/ansible/roles/glitchtip/tasks/create-monitor.yml b/provision-contest/ansible/roles/glitchtip/tasks/create-monitor.yml
new file mode 100644
index 00000000..c8830a5a
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/tasks/create-monitor.yml
@@ -0,0 +1,30 @@
+---
+- name: Fetch the project id
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/teams/domjudge/DOMjudge/projects/"
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ register: glitchtip_proj
+- name: Store JSON query in a fact due to escaping problems in the string below
+ set_fact:
+ TEMP_QUERY0: "[?name=='{{ item }}'].id"
+- name: Create an DOMjudge uptime monitor for the project
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/domjudge/monitors/"
+ status_code: 201
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ body:
+ expectedBody: ""
+ expectedStatus: 200
+ timeout: 5
+ interval: 2
+ monitorType: "GET"
+ url: "https://{{ hostvars[item].ansible_host }}/public"
+ name: "{{ item }}"
+ project_id: "{{ glitchtip_proj.json | community.general.json_query(TEMP_QUERY0) | first }}"
+ body_format: json
diff --git a/provision-contest/ansible/roles/glitchtip/tasks/main.yml b/provision-contest/ansible/roles/glitchtip/tasks/main.yml
new file mode 100644
index 00000000..dbe16dc2
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/tasks/main.yml
@@ -0,0 +1,185 @@
+---
+- name: Create directories
+ file:
+ path: /opt/glitchtip
+ state: directory
+
+- name: Create compose file
+ template:
+ src: templates/docker-compose.yaml.j2
+ dest: /opt/glitchtip/docker-compose.yaml
+
+- name: Deploy GlitchTip compose stack
+ community.docker.docker_compose_v2:
+ project_src: /opt/glitchtip
+ files:
+ - docker-compose.yaml
+
+- name: Assume we don't have an account if we didn't specify the token
+ when: not GLITCHTIP_TOKEN
+ block:
+ - name: Wait for stable GlitchTip migrations
+ ansible.builtin.wait_for:
+ timeout: 10
+
+ - name: Fetch CSRF from login page
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/docs"
+ register: glitchtip_csrf
+
+ - name: Register DOMjudge account
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/_allauth/browser/v1/auth/signup"
+ status_code: 200
+ headers:
+ Cookie: "{{ glitchtip_csrf.cookies_string }}"
+ X-CSRFTOKEN: "{{ glitchtip_csrf.cookies_string | regex_search('csrftoken=([a-zA-Z0-9]+)', '\\1') | first }}"
+ body:
+ email: "team@domjudge.org"
+ password: "{{ GLITCHTIP_PASSWORD }}"
+ body_format: json
+ register: glitchtip_register
+
+ - name: Create API token
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/api-tokens/"
+ status_code: 201
+ headers:
+ Cookie: "{{ glitchtip_register.cookies_string }}"
+ X-CSRFTOKEN: "{{ glitchtip_register.cookies_string | regex_search('csrftoken=([a-zA-Z0-9]+)', '\\1') | first }}"
+ body:
+ label: "ansible"
+ scopes: [
+ "project:read",
+ "project:write",
+ "project:admin",
+ "project:releases",
+ "team:read",
+ "team:write",
+ "team:admin",
+ "event:read",
+ "event:write",
+ "event:admin",
+ "org:read",
+ "org:write",
+ "org:admin",
+ "member:read",
+ "member:write",
+ "member:admin"
+ ]
+ body_format: json
+ register: glitchtip_token
+
+ - name: Set API token
+ ansible.builtin.set_fact:
+ GLITCHTIP_TOKEN: "{{ glitchtip_token.json.token }}"
+
+- name: Check for existing organizations
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/"
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ register: glitchtip_org
+
+- name: Create DOMjudge organization
+ when: glitchtip_org.json | community.general.json_query("[?name=='DOMjudge']") == []
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/"
+ status_code: 201
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ body:
+ name: "DOMjudge"
+ body_format: json
+ register: glitchtip_org
+
+- name: Check for existing teams
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/domjudge/teams/"
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ register: glitchtip_team
+
+- name: Create DOMjudge team
+ when: glitchtip_team.json | community.general.json_query("[?slug=='DOMjudge']") == []
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/domjudge/teams/"
+ status_code: 201
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ body:
+ slug: "DOMjudge"
+ body_format: json
+ register: glitchtip_team
+
+- name: Check for existing projects
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/teams/domjudge/DOMjudge/projects/"
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ register: glitchtip_proj
+
+- name: Create DOMjudge projects
+ when: glitchtip_proj.json | community.general.json_query("[?name=='{{ item }}']") == []
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/teams/domjudge/DOMjudge/projects/"
+ status_code: 201
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ body:
+ name: "{{ item }}"
+ platform: "php-symfony"
+ body_format: json
+ loop: "{{ ['setup-phase'] + groups['domserver'] }}"
+
+- name: Create project dj_notify webhook
+ when: glitchtip_proj.json | community.general.json_query("[?name=='{{ item }}']") == []
+ ansible.builtin.uri:
+ method: POST
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/projects/domjudge/{{ item }}/alerts/"
+ status_code: 201
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ body:
+ name: "dj_notify"
+ alertRecipients:
+ - recipientType: "webhook"
+ url: "http://{{ GLITCHTIP_WEBHOOK_DOMAIN }}:9999/"
+ timespanMinutes: 1
+ quantity: 1
+ uptime: true
+ body_format: json
+ loop: "{{ ['setup-phase'] + groups['domserver'] }}"
+
+- name: Check for existing monitors
+ ansible.builtin.uri:
+ method: GET
+ return_content: yes
+ url: "http://localhost:{{ GLITCHTIP_PORT }}/api/0/organizations/domjudge/monitors/"
+ headers:
+ Authorization: "Bearer {{ GLITCHTIP_TOKEN }}"
+ register: glitchtip_mon
+
+- name: Create DOMjudge monitors
+ when: glitchtip_mon.json | community.general.json_query("[?name=='{{ item }}']") == []
+ loop: "{{ groups['domserver'] }}"
+ include_tasks: create-monitor.yml
diff --git a/provision-contest/ansible/roles/glitchtip/templates/docker-compose.yaml.j2 b/provision-contest/ansible/roles/glitchtip/templates/docker-compose.yaml.j2
new file mode 100644
index 00000000..64e0898b
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/templates/docker-compose.yaml.j2
@@ -0,0 +1,57 @@
+# Uncomment version if using an older version of docker compose
+# version: "3.8"
+x-environment:
+ &default-environment
+ DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
+ SECRET_KEY: {{ GLITCHTIP_SECRET }}
+ PORT: {{ GLITCHTIP_PORT }}
+ EMAIL_URL: consolemail://
+ GLITCHTIP_DOMAIN: http://glitchtip:{{ GLITCHTIP_PORT }}
+ DEFAULT_FROM_EMAIL: email@glitchtip
+ CELERY_WORKER_AUTOSCALE: "1,1"
+ CELERY_WORKER_MAX_TASKS_PER_CHILD: "1000"
+ REQUESTS_CA_BUNDLE: /etc/ssl/certs/ca-certificates.crt
+
+x-depends_on:
+ &default-depends_on
+ - postgres
+ - redis
+
+services:
+ postgres:
+ image: postgres:16-alpine
+ environment:
+ POSTGRES_HOST_AUTH_METHOD: "trust" # Consider removing this and setting a password
+ restart: unless-stopped
+ volumes:
+ - pg-data:/var/lib/postgresql/data
+ redis:
+ image: redis:7-alpine
+ restart: unless-stopped
+ web:
+ image: glitchtip/glitchtip:v4.1.3
+ depends_on: *default-depends_on
+ ports:
+ - {{ GLITCHTIP_PORT }}:{{ GLITCHTIP_PORT }}
+ environment: *default-environment
+ restart: unless-stopped
+ volumes:
+ - uploads:/code/uploads
+ worker:
+ image: glitchtip/glitchtip:v4.1.3
+ command: ./bin/run-celery-with-beat.sh
+ depends_on: *default-depends_on
+ environment: *default-environment
+ restart: unless-stopped
+ volumes:
+ - uploads:/code/uploads
+ - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro
+ migrate:
+ image: glitchtip/glitchtip:v4.1.3
+ depends_on: *default-depends_on
+ command: ./bin/run-migrate.sh
+ environment: *default-environment
+
+volumes:
+ pg-data:
+ uploads:
diff --git a/provision-contest/ansible/roles/glitchtip/vars/.gitignore b/provision-contest/ansible/roles/glitchtip/vars/.gitignore
new file mode 100644
index 00000000..1cda54be
--- /dev/null
+++ b/provision-contest/ansible/roles/glitchtip/vars/.gitignore
@@ -0,0 +1 @@
+*.yml
diff --git a/provision-contest/ansible/roles/grafana/defaults/main.yml b/provision-contest/ansible/roles/grafana/defaults/main.yml
index 804c3e1b..7d90ce04 100644
--- a/provision-contest/ansible/roles/grafana/defaults/main.yml
+++ b/provision-contest/ansible/roles/grafana/defaults/main.yml
@@ -1,2 +1,2 @@
-grafana_port: 8443
+grafana_port: 9443
loki: false
diff --git a/provision-contest/ansible/roles/grafana/files/.gitignore b/provision-contest/ansible/roles/grafana/files/.gitignore
index 87ce969a..bc581906 100644
--- a/provision-contest/ansible/roles/grafana/files/.gitignore
+++ b/provision-contest/ansible/roles/grafana/files/.gitignore
@@ -1 +1,3 @@
/ssl.*
+/grafana.deb
+/loki-linux-amd64.zip
diff --git a/provision-contest/ansible/roles/grafana/files/loki-local-config.yaml b/provision-contest/ansible/roles/grafana/files/loki-local-config.yaml
index b52f9864..b714edb6 100644
--- a/provision-contest/ansible/roles/grafana/files/loki-local-config.yaml
+++ b/provision-contest/ansible/roles/grafana/files/loki-local-config.yaml
@@ -2,45 +2,26 @@ auth_enabled: false
server:
http_listen_port: 3100
- grpc_listen_port: 9096
+ grpc_listen_port: 13100
-ingester:
- lifecycler:
- address: 127.0.0.1
- ring:
- kvstore:
- store: inmemory
- replication_factor: 1
- final_sleep: 0s
- chunk_idle_period: 5m
- chunk_retain_period: 30s
- max_transfer_retries: 0
+common:
+ ring:
+ instance_addr: 127.0.0.1
+ kvstore:
+ store: inmemory
+ replication_factor: 1
+ path_prefix: /tmp/loki
schema_config:
configs:
- - from: 2018-04-15
- store: boltdb
+ - from: 2020-05-15
+ store: tsdb
object_store: filesystem
- schema: v11
+ schema: v13
index:
prefix: index_
- period: 168h
+ period: 24h
storage_config:
- boltdb:
- directory: /data/loki/index
-
filesystem:
- directory: /data/loki/chunks
-
-limits_config:
- enforce_metric_name: false
- reject_old_samples: true
- reject_old_samples_max_age: 168h
-
-chunk_store_config:
- max_look_back_period: 0s
-
-table_manager:
- retention_deletes_enabled: false
- retention_period: 0s
+ directory: /tmp/loki/chunks
diff --git a/provision-contest/ansible/roles/grafana/handlers/main.yml b/provision-contest/ansible/roles/grafana/handlers/main.yml
index d2a23bee..b3fe0a7f 100644
--- a/provision-contest/ansible/roles/grafana/handlers/main.yml
+++ b/provision-contest/ansible/roles/grafana/handlers/main.yml
@@ -1,31 +1,29 @@
---
-# Define here handlers associated to this role.
-
-- name: restart nginx-exporter
+- name: Restart nginx-exporter
service:
name: prometheus-nginx-exporter
enabled: true
state: restarted
-- name: restart php-exporter
+- name: Restart php-exporter
service:
name: php-fpm-exporter
enabled: true
state: restarted
-- name: restart nginx
+- name: Restart prometheus
service:
- name: nginx
+ name: prometheus
enabled: true
state: restarted
-- name: restart grafana
+- name: Restart grafana
service:
name: grafana-server
enabled: true
state: restarted
-- name: restart loki
+- name: Restart loki
service:
name: loki
enabled: true
diff --git a/provision-contest/ansible/roles/grafana/tasks/main.yml b/provision-contest/ansible/roles/grafana/tasks/main.yml
index ea84629a..b7453422 100644
--- a/provision-contest/ansible/roles/grafana/tasks/main.yml
+++ b/provision-contest/ansible/roles/grafana/tasks/main.yml
@@ -1,16 +1,5 @@
---
-- name: Add upstream prometheus apt key
- apt_key:
- url: https://packagecloud.io/the_asten/prometheus/gpgkey
- state: present
-
-- name: Add upstream prometheus apt repo
- apt_repository:
- repo: deb https://packagecloud.io/the_asten/prometheus/ubuntu/ focal main
- state: present
- register: prometheus_repo
-
-- name: install dependencies
+- name: Install dependencies
apt:
state: present
install_recommends: false
@@ -20,108 +9,131 @@
# Setup Prometheus, which scrapes all metrics from the monitored machines
# The client exposes the metrics (on different ports/URIs) and we collect those
-- name: set up prometheus scrapes
+- name: Set up prometheus scrapes
template:
src: prometheus.yml.j2
dest: /etc/prometheus/prometheus.yml
mode: 0644
owner: root
group: root
- notify: restart prometheus
+ notify: Restart prometheus
# Setup loki which gathers our logs
-- name: Install loki
- unarchive:
- src: https://github.com/grafana/loki/releases/download/v2.5.0/loki-linux-amd64.zip
- dest: /usr/bin/
- remote_src: true
- owner: domjudge
- group: domjudge
- when: loki
-
-- name: Dir for loki settings
- file:
- state: directory
- path: /etc/grafana/loki/
- owner: root
- group: root
- mode: 0755
- when: loki
-
-- name: Set loki settings
- copy:
- src: loki-local-config.yaml
- dest: /etc/grafana/loki/
- owner: root
- group: root
- mode: 0644
- when: loki
- notify: restart loki
-
-- name: Setup loki systemd
- copy:
- src: loki.service
- dest: /etc/systemd/system/
- mode: 0655
- when: loki
- notify: restart loki
-
-- name: Start loki service
- service:
- name: loki
- state: started
- enabled: true
+- name: Setup loki
when: loki
+ block:
+ - name: Install loki
+ unarchive:
+ src: loki-linux-amd64.zip
+ dest: /usr/bin/
+ remote_src: false
+ owner: domjudge
+ group: domjudge
+ when: ICPC_IMAGE
+
+ - name: Install loki
+ unarchive:
+ src: https://github.com/grafana/loki/releases/download/v2.5.0/loki-linux-amd64.zip
+ dest: /usr/bin/
+ remote_src: true
+ owner: domjudge
+ group: domjudge
+ when: not ICPC_IMAGE
+
+ - name: Dir for loki settings
+ file:
+ state: directory
+ path: /etc/grafana/loki/
+ owner: root
+ group: root
+ mode: 0755
+
+ - name: Set loki settings
+ copy:
+ src: loki-local-config.yaml
+ dest: /etc/grafana/loki/
+ owner: root
+ group: root
+ mode: 0644
+ notify: Restart loki
+
+ - name: Setup loki systemd
+ copy:
+ src: loki.service
+ dest: /etc/systemd/system/
+ mode: 0655
+ notify: Restart loki
+
+ - name: Start loki service
+ service:
+ name: loki
+ state: started
+ enabled: true
## Setup grafana
- name: Install grafana
+ when: not WF_RESTRICTED_NETWORK
apt:
- deb: https://dl.grafana.com/enterprise/release/grafana-enterprise_8.4.6_amd64.deb
+ deb: https://dl.grafana.com/oss/release/grafana_11.1.3_amd64.deb
state: present
- notify: restart grafana
-
-- name: configure grafana
+ notify: Restart grafana
+
+- name: Install grafana from local deb
+ when: WF_RESTRICTED_NETWORK
+ block:
+ - name: Copy local grafana deb to monitoring host
+ copy:
+ src: grafana.deb
+ dest: /srv/grafana.deb
+
+ - name: Install local archive on remote
+ apt:
+ deb: /srv/grafana.deb
+ state: present
+ notify: Restart grafana
+
+- name: Configure grafana
synchronize:
src: files/grafana/environment
dest: /etc/default/grafana-server
- notify: restart grafana
+ notify: Restart grafana
-- name: set up grafana datasources
- synchronize:
- src: files/grafana/datasources.yml
+- name: Set up grafana datasources
+ template:
+ src: datasources.yml.j2
dest: /etc/grafana/provisioning/datasources/default.yml
- notify: restart grafana
+ notify: Restart grafana
-- name: set up grafana dashboards
+- name: Set up grafana dashboards
synchronize:
src: files/grafana/dashboards.yml
dest: /etc/grafana/provisioning/dashboards/default.yml
- notify: restart grafana
+ notify: Restart grafana
-- name: copy grafana dashboards
+- name: Copy grafana dashboards
synchronize:
src: dashboards/
dest: /etc/grafana/dashboards/
- notify: restart grafana
+ notify: Restart grafana
# Setup nginx with selfsigned certificate
-- name: copy ssl cert
+- name: Copy ssl cert
synchronize:
src: ssl.crt
dest: /etc/ssl/certs/grafana.crt
- notify: restart nginx
+ notify: Restart nginx
-- name: copy ssl key
+- name: Copy ssl key
synchronize:
src: ssl.key
dest: /etc/ssl/private/grafana.key
- notify: restart nginx
+ notify: Restart nginx
-- name: copy default nginx config
+- name: Copy default nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-enabled/grafana.conf
owner: root
group: root
mode: 0644
- notify: restart nginx
+ notify: Restart nginx
diff --git a/provision-contest/ansible/roles/grafana/files/datasources.yml b/provision-contest/ansible/roles/grafana/templates/datasources.yml.j2
similarity index 86%
rename from provision-contest/ansible/roles/grafana/files/datasources.yml
rename to provision-contest/ansible/roles/grafana/templates/datasources.yml.j2
index 36088aff..fe32e974 100644
--- a/provision-contest/ansible/roles/grafana/files/datasources.yml
+++ b/provision-contest/ansible/roles/grafana/templates/datasources.yml.j2
@@ -48,3 +48,14 @@ datasources:
version: 1
# allow users to edit datasources from the UI.
editable: true
+{% if loki is defined and loki %}
+ - name: Loki
+ type: loki
+ access: proxy
+ url: http://localhost:3100
+ jsonData:
+ timeout: 60
+ maxLines: 1000
+ httpHeaderName1: Connection
+ httpHeaderName2: Upgrade
+{% endif %}
diff --git a/provision-contest/ansible/roles/grafana/templates/nginx.conf.j2 b/provision-contest/ansible/roles/grafana/templates/nginx.conf.j2
index 3be8301b..8550b6a5 100644
--- a/provision-contest/ansible/roles/grafana/templates/nginx.conf.j2
+++ b/provision-contest/ansible/roles/grafana/templates/nginx.conf.j2
@@ -1,5 +1,5 @@
server {
- listen 8443 ssl http2;
+ listen {{ grafana_port }} ssl http2;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -21,7 +21,7 @@ server {
proxy_redirect off;
proxy_set_header Host $host;
}
-{% endif $}
+{% endif %}
access_log /var/log/nginx/prometheus.log;
error_log /var/log/nginx/prometheus.log;
diff --git a/provision-contest/ansible/roles/grafana/templates/prometheus.yml.j2 b/provision-contest/ansible/roles/grafana/templates/prometheus.yml.j2
index 33e22df8..8352c07c 100644
--- a/provision-contest/ansible/roles/grafana/templates/prometheus.yml.j2
+++ b/provision-contest/ansible/roles/grafana/templates/prometheus.yml.j2
@@ -5,86 +5,158 @@ global:
scrape_configs:
- job_name: 'grafana'
static_configs:
- - targets: ['localhost:443']
+ - targets: ['localhost:3000']
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- - job_name: 'db'
+{% for group_prefix in GROUP_PREFIXES %}
+{% if groups[group_prefix+"judgehost"][0] is defined %}
+ - job_name: node_judgehost
+ basic_auth:
+ username: "prometheus"
+ password: "{{ hostvars[groups[group_prefix+"judgehost"][0]]['PROMETHEUS_PASS'] }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
-{% for host in groups["domserver"] %}
+{% for host in groups[group_prefix+"judgehost"] %}
+ - {{ hostvars[host].ansible_host }}:9100
+{% endfor %}
+{% endif %}
+{% if groups["domserver"][0] is defined %}
+ - job_name: {{ group_prefix ~ domjudge }}
+ basic_auth:
+ username: admin
+ password: {{ ADMIN_PASSWORD }}
+ metrics_path: /api/v4/metrics/prometheus
+ scheme: https
+ tls_config:
+ insecure_skip_verify: true
+ static_configs:
+ - targets:
+{% for host in groups[group_prefix+"domserver"] %}
+ - {{ hostvars[host].ansible_host }}
+{% endfor %}
+ - job_name: {{ group_prefix ~ 'db' }}
+ basic_auth:
+ username: "prometheus"
+ password: "{{ hostvars[groups[group_prefix+"domserver"][0]]['PROMETHEUS_PASS'] }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
+ static_configs:
+ - targets:
+{% for host in groups[group_prefix+"domserver"] %}
- {{ hostvars[host].ansible_host }}:9104
{% endfor %}
- - job_name: node_domserver
+ - job_name: {{ group_prefix ~ 'node_domserver' }}
+ basic_auth:
+ username: "prometheus"
+ password: "{{ hostvars[groups[group_prefix+"domserver"][0]]['PROMETHEUS_PASS'] }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["domserver"] %}
- {{ hostvars[host].ansible_host }}:9100
{% endfor %}
- - job_name: node_judgehost
+ - job_name: {{ group_prefix ~ 'web_nginx_domserver' }}
+ basic_auth:
+ username: "prometheus"
+ password: "{{ hostvars[groups[group_prefix+"domserver"][0]]['PROMETHEUS_PASS'] }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
-{% for host in groups["judgehost"] %}
- - {{ hostvars[host].ansible_host }}:9100
+{% for host in groups[group_prefix+"domserver"] %}
+ - {{ hostvars[host].ansible_host }}:9113
+{% endfor %}
+ - job_name: {{ group_prefix ~ 'web_fpm_domserver' }}
+ basic_auth:
+ username: "prometheus"
+ password: "{{ hostvars[groups[group_prefix+"domserver"][0]]['PROMETHEUS_PASS'] }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
+ static_configs:
+ - targets:
+{% for host in groups[group_prefix+"domserver"] %}
+ - {{ hostvars[host].ansible_host }}:9253
+{% endfor %}
+{% endif %}
{% endfor %}
- job_name: node_grafana
+ basic_auth:
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["grafana"] %}
- {{ hostvars[host].ansible_host }}:9100
{% endfor %}
- job_name: node_mgmt
+ basic_auth:
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["mgmt"] %}
- {{ hostvars[host].ansible_host }}:9100
{% endfor %}
- job_name: node_scoreboard
+ basic_auth:
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["scoreboard"] %}
- {{ hostvars[host].ansible_host }}:9100
{% endfor %}
- job_name: node_cds
+ basic_auth:
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["cds"] %}
- {{ hostvars[host].ansible_host }}:9100
{% endfor %}
- - job_name: domjudge
+ - job_name: 'web_nginx_cds'
basic_auth:
- username: admin
- password: {{ ADMIN_PASSWORD }}
- metrics_path: /api/v4/metrics/prometheus
- scheme: https
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
tls_config:
insecure_skip_verify: true
- static_configs:
- - targets:
-{% for host in groups["domserver"] %}
- - {{ hostvars[host].ansible_host }}
-{% endfor %}
- - job_name: 'web_nginx_cds'
+ scheme: https
static_configs:
- targets:
{% for host in groups["cds"] %}
- {{ hostvars[host].ansible_host }}:9113
{% endfor %}
- job_name: 'web_nginx_scoreboard'
+ basic_auth:
+ username: "prometheus"
+ password: "{{ PROMETHEUS_PASS }}"
+ tls_config:
+ insecure_skip_verify: true
+ scheme: https
static_configs:
- targets:
{% for host in groups["scoreboard"] %}
- {{ hostvars[host].ansible_host }}:9113
-{% endfor %}
- - job_name: 'web_nginx_domserver'
- static_configs:
- - targets:
-{% for host in groups["domserver"] %}
- - {{ hostvars[host].ansible_host }}:9113
-{% endfor %}
- - job_name: 'web_fpm_domserver'
- static_configs:
- - targets:
-{% for host in groups["domserver"] %}
- - {{ hostvars[host].ansible_host }}:9253
{% endfor %}
diff --git a/provision-contest/ansible/roles/hosts/tasks/main.yml b/provision-contest/ansible/roles/hosts/tasks/main.yml
index 825265ed..3193aa7e 100644
--- a/provision-contest/ansible/roles/hosts/tasks/main.yml
+++ b/provision-contest/ansible/roles/hosts/tasks/main.yml
@@ -1,19 +1,14 @@
---
# These tasks add entries to /etc/hosts
-# FIXME: this needs to be cleaned up and done in a way such that the
-# tasks are not marked as "changed" every time they are run, and also
-# work better with ad-hoc editing of the inventory hosts file.
+- name: Set the hostname based on inventory
+ hostname:
+ name: "{{ inventory_hostname }}"
-- name: add all hosts to hosts file
- lineinfile:
+- name: Set new hosts file
+ template:
+ src: hosts.j2
dest: /etc/hosts
- regexp: '{{ item }}$'
- line: "{{ hostvars[item].ansible_host }} {{ item }}"
- loop: "{{ groups['all'] }}"
-
-- name: add domjudge in hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: 'domserver'
- line: "{{ DOMSERVER_IP }} domjudge domserver"
+ group: root
+ owner: root
+ mode: 0644
diff --git a/provision-contest/ansible/roles/hosts/templates/hosts.j2 b/provision-contest/ansible/roles/hosts/templates/hosts.j2
new file mode 100644
index 00000000..d1218d9e
--- /dev/null
+++ b/provision-contest/ansible/roles/hosts/templates/hosts.j2
@@ -0,0 +1,24 @@
+127.0.0.1 localhost
+127.0.1.1 {{ inventory_hostname }}
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+
+{% if DOMSERVER_IP is defined %}
+{{ DOMSERVER_IP }} domjudge domserver
+{% endif %}
+{% for item in groups['all'] %}
+{% if hostvars[item].ansible_host is defined %}
+{{ hostvars[item].ansible_host }} {{ item }}
+{% endif %}
+{% endfor %}
+
+{% if WF_RESTRICTED_NETWORK %}
+{% for host, ip_address in HOSTS.items() %}
+{{ ip_address }} {{ host }}
+{% endfor %}
+{% endif %}
diff --git a/provision-contest/ansible/roles/icpc_fixes/handlers/main.yml b/provision-contest/ansible/roles/icpc_fixes/handlers/main.yml
index d3c5d9d3..3e5568d5 100644
--- a/provision-contest/ansible/roles/icpc_fixes/handlers/main.yml
+++ b/provision-contest/ansible/roles/icpc_fixes/handlers/main.yml
@@ -1,7 +1,5 @@
---
-# Define here handlers associated to this role.
-
-- name: restart systemd timesyncd
+- name: Restart systemd timesyncd
systemd:
name: systemd-timesyncd
state: restarted
diff --git a/provision-contest/ansible/roles/icpc_fixes/tasks/main.yml b/provision-contest/ansible/roles/icpc_fixes/tasks/main.yml
index 862eed55..28a9d7b6 100644
--- a/provision-contest/ansible/roles/icpc_fixes/tasks/main.yml
+++ b/provision-contest/ansible/roles/icpc_fixes/tasks/main.yml
@@ -1,29 +1,35 @@
---
# These tasks perform fixes used on the ICPC image to the base system.
-- name: add NTP servers
+- name: Add NTP servers
lineinfile:
dest: /etc/systemd/timesyncd.conf
regexp: '^#?NTP='
- line: "NTP=10.3.3.208 10.3.3.209"
- notify: restart systemd timesyncd
+ line: "NTP=ntp1 ntp2"
+ notify: Restart systemd timesyncd
when: WF_RESTRICTED_NETWORK
-- name: remove source line from interfaces
- lineinfile:
- dest: /etc/network/interfaces
- regexp: '^source-'
- state: absent
-
- name: Re-order PXEboot
command: efibootmgr -o {{ EFI_ORDER }}
when: EFI_ORDER is defined
-- name: disable mumble,selfie services
- service:
- name: "{{ item }}"
- state: stopped
- enabled: false
- loop:
- - mumble-server
- - selfie
+- name: IPv6 nginx fixes
+ block:
+ - name: Create nginx directory before installation
+ file:
+ path: /etc/{{ item }}
+ state: directory
+ mode: '0755'
+ group: root
+ owner: root
+ loop:
+ - nginx
+ - nginx/sites-enabled
+
+ - name: Create nginx default without IPv6 listener
+ copy:
+ dest: /etc/nginx/sites-enabled/default
+ mode: '0644'
+ group: root
+ owner: root
+ content: ""
diff --git a/provision-contest/ansible/roles/judgedaemon/vars/main.yml b/provision-contest/ansible/roles/judgedaemon/defaults/main.yml
similarity index 100%
rename from provision-contest/ansible/roles/judgedaemon/vars/main.yml
rename to provision-contest/ansible/roles/judgedaemon/defaults/main.yml
diff --git a/provision-contest/ansible/roles/judgedaemon/files/chroot-list/.gitignore b/provision-contest/ansible/roles/judgedaemon/files/chroot-list/.gitignore
new file mode 100644
index 00000000..970eaaab
--- /dev/null
+++ b/provision-contest/ansible/roles/judgedaemon/files/chroot-list/.gitignore
@@ -0,0 +1,4 @@
+*.list
+*.list.gpg
+*.list.asc
+*.list.arm
diff --git a/provision-contest/ansible/roles/judgedaemon/files/tune_cpu.service b/provision-contest/ansible/roles/judgedaemon/files/tune_cpu.service
index 02e35a61..354d479e 100644
--- a/provision-contest/ansible/roles/judgedaemon/files/tune_cpu.service
+++ b/provision-contest/ansible/roles/judgedaemon/files/tune_cpu.service
@@ -1,12 +1,12 @@
[Unit]
Description=Tune CPU cores: disable turboboost, downclocking, etc.
+Before=domjudge-judgedaemon.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/disable-turboboost_ht
RemainAfterExit=true
ExecStartPre=/bin/sleep 10
-Before=domjudge-judgedaemon.target
[Install]
WantedBy=multi-user.target
diff --git a/provision-contest/ansible/roles/judgedaemon/handlers/main.yml b/provision-contest/ansible/roles/judgedaemon/handlers/main.yml
index 5676e7b2..6973da29 100644
--- a/provision-contest/ansible/roles/judgedaemon/handlers/main.yml
+++ b/provision-contest/ansible/roles/judgedaemon/handlers/main.yml
@@ -1,29 +1,28 @@
---
-# Define here handlers associated to this role.
-
-- name: enable and restart tune_cpu
+- name: Enable and restart tune_cpu
systemd:
name: tune_cpu
enabled: true
state: restarted
daemon_reload: true
-- name: enable and restart create-cgroups
+- name: Enable and restart create-cgroups
service:
name: create-cgroups
enabled: true
state: restarted
daemon_reload: true
-- name: enable and restart judgedaemon
+- name: Enable and restart judgedaemon
service:
name: "domjudge-judgehost.target"
enabled: true
state: restarted
daemon_reload: true
-- name: update grub
+- name: Update grub
command: update-grub
-- name: reboot
+- name: Reboot
reboot:
+ search_paths: ['/lib/molly-guard', '/sbin']
diff --git a/provision-contest/ansible/roles/judgedaemon/meta/main.yml b/provision-contest/ansible/roles/judgedaemon/meta/main.yml
index 8a41d13b..15c87ace 100644
--- a/provision-contest/ansible/roles/judgedaemon/meta/main.yml
+++ b/provision-contest/ansible/roles/judgedaemon/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: domjudge_build
+ tags: domjudge_build
diff --git a/provision-contest/ansible/roles/judgedaemon/tasks/main.yml b/provision-contest/ansible/roles/judgedaemon/tasks/main.yml
index 8906e2a2..e585b7e2 100644
--- a/provision-contest/ansible/roles/judgedaemon/tasks/main.yml
+++ b/provision-contest/ansible/roles/judgedaemon/tasks/main.yml
@@ -1,14 +1,21 @@
---
# These tasks configure the DOMjudge judgedaemon with chroot
-- name: create domjudge-run users
- user: name=domjudge-run-{{ item }} createhome=no home=/nonexistent group=nogroup shell=/bin/false
+- name: Create domjudge-run users
+ user:
+ name: "domjudge-run-{{ item }}"
+ createhome: false
+ home: /nonexistent
+ group: nogroup
+ shell: /bin/false
loop: "{{ cpucore }}"
-- name: create domjudge-run group
- group: name=domjudge-run state=present
+- name: Create domjudge-run group
+ group:
+ name: domjudge-run
+ state: present
-- name: create temp package directory
+- name: Create temp package directory
file:
path: /tmp/dj_ansible
state: directory
@@ -16,46 +23,76 @@
group: root
mode: 0700
-- name: copy chroot DEB packages to install
+- name: Copy chroot DEB packages to install
synchronize:
src: install-chroot
dest: /tmp/dj_ansible/
-- name: create chroot
- shell: "set -o pipefail && {{ DJ_DIR }}/misc-tools/dj_make_chroot -y -i openjdk-11-jdk-headless -l \"$(ls /tmp/dj_ansible/install-chroot/*.deb 2>/dev/null | tr '\n' ',')\" 2>&1 | tee /tmp/dj_make_chroot.log; grep '^Done building chroot in' /tmp/dj_make_chroot.log"
+- name: Copy chroot-list to install
+ synchronize:
+ src: chroot-list
+ dest: /tmp/dj_ansible/
+
+- name: Create chroot
+ shell: "set -o pipefail &&
+ {{ DJ_DIR }}/misc-tools/dj_make_chroot -y -H
+ -i icpc-kotlinc,openjdk-17-jdk-headless
+ -l \"$(ls /tmp/dj_ansible/install-chroot/*.deb 2>/dev/null | tr '\n' ',')\"
+ -s \"$(ls /tmp/dj_ansible/chroot-list/*.list 2>/dev/null | tr '\n' ',')\"
+ 2>&1 | tee /tmp/dj_make_chroot.log;
+ grep '^Done building chroot in' /tmp/dj_make_chroot.log"
environment:
- DEBMIRROR: "{{ 'https://packages/ubuntu' if WF_RESTRICTED_NETWORK else '' }}"
+ DEBMIRROR: "{%- if WF_RESTRICTED_NETWORK and ICPC_IMAGE -%}https://packages/ubuntu
+ {%- elif ICPC_IMAGE -%}https://sysopspackages.icpc.global/ubuntu
+ {%- else -%}
+ {%- endif -%}"
args:
+ executable: /bin/bash
creates: "/chroot/domjudge"
- name: Pre-generate the kernel flags for ansible usage
set_fact:
- procline: "cgroup_enable=memory swapaccount=1 isolcpus={{ cpucore|join(',') }}"
+ procline: "apparmor=0 systemd.unified_cgroup_hierarchy=0 cgroup_enable=memory swapaccount=1 isolcpus={{ cpucore | join(',') }}"
-- name: add cgroup kernel parameters
+- name: Add cgroup kernel parameters
lineinfile:
dest: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT='
line: "GRUB_CMDLINE_LINUX_DEFAULT=\"{{ procline }}\""
-- name: check cgroup kernel parameters
+- name: Pre-generate the kernel flags for ansible usage (specific AWS)
+ # AWS machines overwrite the variable from the last step in another file
+ # console: https://tldp.org/HOWTO/Remote-Serial-Console-HOWTO/configure-kernel.html
+ # nvme_core: https://docs.aws.amazon.com/ebs/latest/userguide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
+ when: AWS
+ set_fact:
+ procline_aws: "console=tty1 console=ttyS0 nvme_core.io_timeout=4294967295 {{ procline }}"
+
+- name: Add cgroup kernel parameters for AWS machines
+ when: AWS
+ lineinfile:
+ dest: /etc/default/grub.d/50-cloudimg-settings.cfg
+ regexp: '^GRUB_CMDLINE_LINUX_DEFAULT='
+ line: "GRUB_CMDLINE_LINUX_DEFAULT=\"{{ procline_aws }}\""
+
+- name: Check cgroup kernel parameters
command: cat /proc/cmdline
register: kernel_cmdline
changed_when: procline not in kernel_cmdline.stdout
notify:
- - update grub
- - reboot
+ - Update grub
+ - Reboot
-- name: reboot to activate kernel parameters
+- name: Reboot to activate kernel parameters
meta: flush_handlers
-- name: copy script to disable turboboost and hyperthreading
+- name: Copy script to disable turboboost and hyperthreading
copy:
src: disable-turboboost_ht
dest: /usr/local/sbin/
mode: 0755
-- name: copy tune_cpu systemd unit file
+- name: Copy tune_cpu systemd unit file
copy:
src: tune_cpu.service
dest: /etc/systemd/system/
@@ -63,9 +100,13 @@
group: root
owner: root
notify:
- - enable and restart tune_cpu
+ - Enable and restart tune_cpu
-- name: copy judgedaemon related systemd unit files
+- name: Enable and restart tune_cpu (flushing as this should always happen even if we fail further)
+ # We need a daemon-reload, so we loose idempotency if we start the service in the next block
+ ansible.builtin.meta: flush_handlers
+
+- name: Copy judgedaemon related systemd unit files
copy:
remote_src: true
src: "{{ DJ_DIR }}/lib/judge/{{ item }}.service"
@@ -77,9 +118,9 @@
- create-cgroups
- domjudge-judgedaemon@
notify:
- - enable and restart create-cgroups
+ - Enable and restart create-cgroups
-- name: template judgedaemon template systemd unit file
+- name: Template judgedaemon template systemd unit file
template:
src: domjudge-judgehost.target.j2
dest: /etc/systemd/system/domjudge-judgehost.target
@@ -87,9 +128,9 @@
group: root
owner: root
notify:
- - enable and restart judgedaemon
+ - Enable and restart judgedaemon
-- name: disable systemd timers
+- name: Disable systemd timers
systemd:
name: "{{ item }}"
masked: true
@@ -97,3 +138,10 @@
- apt-daily-upgrade.timer
- apt-daily.timer
- systemd-tmpfiles-clean.timer
+
+- name: Enable and start judgedaemon
+ service:
+ name: "domjudge-judgehost.target"
+ enabled: true
+ state: started
+ daemon_reload: true
diff --git a/provision-contest/ansible/roles/judgedaemon/templates/domjudge-judgehost.target.j2 b/provision-contest/ansible/roles/judgedaemon/templates/domjudge-judgehost.target.j2
index d39dc784..490dc602 100644
--- a/provision-contest/ansible/roles/judgedaemon/templates/domjudge-judgehost.target.j2
+++ b/provision-contest/ansible/roles/judgedaemon/templates/domjudge-judgehost.target.j2
@@ -1,6 +1,6 @@
[Unit]
Description=DOMjudge judgehost running one or more judgedaemons
-Requires={% for core in CPUCORE %}domjudge-judgedaemon@{{ core }}.service {% endfor %}
+Requires={% for core in cpucore %}domjudge-judgedaemon@{{ core }}.service {% endfor %}
[Install]
WantedBy=multi-user.target
diff --git a/provision-contest/ansible/roles/keepalived/files/alerting/bin/alert_listener.sh b/provision-contest/ansible/roles/keepalived/files/alerting/bin/alert_listener.sh
index 2afbf4e7..08da6a8b 100755
--- a/provision-contest/ansible/roles/keepalived/files/alerting/bin/alert_listener.sh
+++ b/provision-contest/ansible/roles/keepalived/files/alerting/bin/alert_listener.sh
@@ -3,4 +3,5 @@ while true; do
if [ -f ~/alerting.sh ]; then
~/alerting.sh
fi
+ sleep 0.1s
done
diff --git a/provision-contest/ansible/roles/keepalived/files/alerting/bin/alerting.sh.template b/provision-contest/ansible/roles/keepalived/files/alerting/bin/alerting.sh.template
index a44e77a1..43b58760 100755
--- a/provision-contest/ansible/roles/keepalived/files/alerting/bin/alerting.sh.template
+++ b/provision-contest/ansible/roles/keepalived/files/alerting/bin/alerting.sh.template
@@ -1,7 +1,16 @@
-#!/bin/sh
+#!/bin/bash
+# Try a couple of different notification methods.
+
+notify-send -u critical -t 60000 "Alert: keepalived status changed at $(date '+%T') !"
+
+amixer -c 0 set Master 100%
amixer -c 1 set Master 100%
for i in 1 2 3 4 5 6 7 8 9 10 ; do
aplay /usr/share/sounds/sound-icons/pipe.wav
done
+
+beep -f 500 -l 400 -d 100 -r 4
+beep -f 1000 -l 400 -d 100 -r 4
+beep -f 500 -l 200 -d 100 -r 6
diff --git a/provision-contest/ansible/roles/keepalived/files/alerting/bin/trigger_alert.sh b/provision-contest/ansible/roles/keepalived/files/alerting/bin/trigger_alert.sh
index 6c5c6fb9..72922d00 100755
--- a/provision-contest/ansible/roles/keepalived/files/alerting/bin/trigger_alert.sh
+++ b/provision-contest/ansible/roles/keepalived/files/alerting/bin/trigger_alert.sh
@@ -1,2 +1,7 @@
#!/bin/sh
+
+# This only copies the script below which contains alerting code
+# A desktop autostart application started the alert_listener.sh
+# script, which then detects the copy and executes it.
+
cp ~/bin/alerting.sh.template ~/alerting.sh
diff --git a/provision-contest/ansible/roles/keepalived/handlers/main.yml b/provision-contest/ansible/roles/keepalived/handlers/main.yml
index 47050fb0..730921ea 100644
--- a/provision-contest/ansible/roles/keepalived/handlers/main.yml
+++ b/provision-contest/ansible/roles/keepalived/handlers/main.yml
@@ -1,5 +1,5 @@
---
-# Define here handlers associated to this role.
-
-- name: restart keepalived service
- service: name=keepalived state=restarted
+- name: Restart keepalived service
+ service:
+ name: keepalived
+ state: restarted
diff --git a/provision-contest/ansible/roles/keepalived/meta/main.yml b/provision-contest/ansible/roles/keepalived/meta/main.yml
index 4a4f743a..a480e52b 100644
--- a/provision-contest/ansible/roles/keepalived/meta/main.yml
+++ b/provision-contest/ansible/roles/keepalived/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: domjudge_user
+ tags: domjudge_user
diff --git a/provision-contest/ansible/roles/keepalived/tasks/main.yml b/provision-contest/ansible/roles/keepalived/tasks/main.yml
index 8225b237..ae011b7f 100644
--- a/provision-contest/ansible/roles/keepalived/tasks/main.yml
+++ b/provision-contest/ansible/roles/keepalived/tasks/main.yml
@@ -1,11 +1,12 @@
---
# These tasks install keepalived with IP failover
-- name: Install keepalived
+- name: Install keepalived and beep (for alerting)
apt:
state: present
pkg:
- keepalived
+ - beep
- name: Configure keepalived for domjudge IP failover
template:
@@ -14,7 +15,7 @@
mode: 0644
group: root
owner: root
- notify: restart keepalived service
+ notify: Restart keepalived service
- name: Start but disable keepalived (so it does not fail back to primary)
service:
diff --git a/provision-contest/ansible/roles/keepalived/templates/keepalived.conf.j2 b/provision-contest/ansible/roles/keepalived/templates/keepalived.conf.j2
index 3d241f55..617cdf6c 100644
--- a/provision-contest/ansible/roles/keepalived/templates/keepalived.conf.j2
+++ b/provision-contest/ansible/roles/keepalived/templates/keepalived.conf.j2
@@ -1,17 +1,18 @@
vrrp_instance lb_ipv4 {
state MASTER
- interface {{ ansible_facts['default_ipv4']['interface'] }}
+ interface {{ REPLICATION_INTERFACE|default(ansible_facts['default_ipv4']['interface']) }}
use_vmac
virtual_router_id 32
- priority {{KEEPALIVED_PRIORITY}}
+ priority {{ KEEPALIVED_PRIORITY }}
virtual_ipaddress {
- {{DOMSERVER_IP}}
+ {{ DOMSERVER_IP }}
}
authentication {
auth_type PASS
- auth_pass {{REPLICATION_PASSWORD}}
+ auth_pass {{ REPLICATION_PASSWORD }}
}
- notify_backup /home/domjudge/bin/triggeralert.sh
- notify_master /home/domjudge/bin/triggeralert.sh
- notify_fault /home/domjudge/bin/triggeralert.sh
+ script_user domjudge domjudge
+ notify_backup /home/domjudge/bin/trigger_alert.sh
+ notify_master /home/domjudge/bin/trigger_alert.sh
+ notify_fault /home/domjudge/bin/trigger_alert.sh
}
diff --git a/provision-contest/ansible/roles/mysql_replication/meta/main.yml b/provision-contest/ansible/roles/mysql_replication/meta/main.yml
index 2ecf9284..bbf7aabf 100644
--- a/provision-contest/ansible/roles/mysql_replication/meta/main.yml
+++ b/provision-contest/ansible/roles/mysql_replication/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: mysql_server
+ tags: mysql_server
diff --git a/provision-contest/ansible/roles/mysql_replication/tasks/main.yml b/provision-contest/ansible/roles/mysql_replication/tasks/main.yml
index 45f8b4a6..0f873b66 100644
--- a/provision-contest/ansible/roles/mysql_replication/tasks/main.yml
+++ b/provision-contest/ansible/roles/mysql_replication/tasks/main.yml
@@ -1,31 +1,43 @@
---
# These tasks configure a MySQL server for master/master replication
-- name: install replication helper packages
+- name: Install replication helper packages
apt:
state: present
pkg:
- mariadb-backup
-- name: disable mysql listen on localhost only
+- name: Disable mysql listen on localhost only
replace:
path: /etc/mysql/mariadb.conf.d/50-server.cnf
regexp: '^bind-address\s*='
replace: '#bind-address ='
-- name: add mysql config snippet for replication
+- name: Set the replication interface variable
+ set_fact:
+ replication_interface: "{{ REPLICATION_INTERFACE|default(ansible_default_ipv4.interface) }}"
+
+- name: Set the variable for the IP address of the replication interface
+ set_fact:
+ replication_interface_ip_address: "{{ vars['ansible_' + replication_interface]['ipv4']['address'] }}"
+
+- name: Add mysql config snippet for replication
template:
src: mysql.replication.cnf.j2
dest: /etc/mysql/mariadb.conf.d/zzz_replication.cnf
mode: 0644
group: root
owner: root
- notify: restart mysql
+ notify: Restart mysql
-- name: copy script to setup replication
- template: src=setup-replication.sh.j2 dest=/usr/local/sbin/setup-replication.sh mode=0755
+- name: Copy script to setup replication
+ template:
+ src: setup-replication.sh.j2
+ dest: /usr/local/sbin/setup-replication.sh
+ mode: 0755
-- name: create mysql replication user
+- name: Create mysql replication user
+ no_log: true
mysql_user:
name: replication
host: '%'
@@ -34,5 +46,5 @@
priv: '*.*:REPLICATION SLAVE'
state: present
-- name: make sure mysql is restarted
+- name: Make sure mysql is restarted
meta: flush_handlers
diff --git a/provision-contest/ansible/roles/mysql_replication/templates/mysql.replication.cnf.j2 b/provision-contest/ansible/roles/mysql_replication/templates/mysql.replication.cnf.j2
index 9cb024e4..93f58de0 100644
--- a/provision-contest/ansible/roles/mysql_replication/templates/mysql.replication.cnf.j2
+++ b/provision-contest/ansible/roles/mysql_replication/templates/mysql.replication.cnf.j2
@@ -8,4 +8,4 @@ log-bin = /var/log/mysql/mysql-bin.log
binlog_do_db = domjudge
# Host specific replication configuration
-server_id = {{ ansible_default_ipv4.address | regex_replace('^\d{2,3}\.\d+\.\d+\.', '') }}
+server_id = {{ replication_interface_ip_address | ipaddr('int') }}
diff --git a/provision-contest/ansible/roles/mysql_replication/templates/setup-replication.sh.j2 b/provision-contest/ansible/roles/mysql_replication/templates/setup-replication.sh.j2
index 5be5a45b..6bf515d1 100755
--- a/provision-contest/ansible/roles/mysql_replication/templates/setup-replication.sh.j2
+++ b/provision-contest/ansible/roles/mysql_replication/templates/setup-replication.sh.j2
@@ -1,14 +1,19 @@
-#!/bin/sh -e
+#!/bin/bash -e
# This script sets up MySQL master/master replication on two servers.
usage()
{
cat <
+Usage: $0 [OPTION]...
-This script sets up MySQL master/master replication on two servers.
-Run this script as root on the secondary server and specify the
-primary one as argument. The databases on this server will be wiped!
+Options:
+ -m Set up two-way master/master replication
+ -h Show this usage information
+
+This script sets up MySQL master/slave or master/master replication
+on two servers. Run this script as root on the secondary server and
+specify the primary one as argument. The databases on the secondary
+will be wiped!
This script should be run after ansible code has been run, so that
MySQL replication configuration and users are present on both machines
@@ -17,6 +22,11 @@ Running this script will synchronize the databases on this host to
that on , including user credentials, so make sure that
etc/dbpasswords.secret contains the same credentials on each machine.
+The -m option should be used to set up two-way master/master replication,
+which is normally used between the primary and a secondary hot-standby.
+In case of replication to a disaster recovery laptop, this option should
+normally not be used.
+
To recover a machine after it went down and a failover happened:
1. Bring the machine up again with the keepalived service stopped (it
@@ -24,10 +34,10 @@ To recover a machine after it went down and a failover happened:
2. Make sure the machine is fully configured (using ansible).
-3. Run this script as root on the machine with argument the active
+3. Run this script as root on the failed machine with argument the active
domserver. For example, if the active machine is domjudge-backup
and this, failed machine is presumably domjudge-primary, then run:
- setup-replication domjudge-backup
+ setup-replication -m domjudge-backup
When this script completes successfully (should take ~1-3 minutes
when using a Gbit network and about 1-5 GB database size), the two
machines are running in master/master replication. If it fails,
@@ -42,14 +52,34 @@ To recover a machine after it went down and a failover happened:
on domjudge-primary, and check that the domjudge IP correctly
returned to domjudge-primary again.
+ Alternatively, switch the effective roles of the two by decreasing
+ the priority in /etc/keepalived/keepalived.conf on domjudge-primary
+ and run on it:
+ service keepalived start
+
EOF
}
+SETUP_MASTER_MASTER=0
+while getopts 'mh' OPT ; do
+ case $OPT in
+ m) SETUP_MASTER_MASTER=1 ;;
+ h) SHOWHELP=1 ;;
+ \?) echo "Error: could not parse options."; exit 1 ;;
+ esac
+done
+shift $((OPTIND-1))
+
PRIMARY=$1
+if [ -n "${SHOWHELP}" ]; then
+ usage
+ exit 0
+fi
+
if [ -z "$PRIMARY" ]; then
- echo "Error: specify primary host to set up master/master replication with."
+ echo "Error: specify primary host to set up replication with."
usage
exit 1
fi
@@ -60,8 +90,8 @@ if [ $(id -un) != 'root' ]; then
exit 1
fi
-if ip addr | grep '{{DOMSERVER_IP}}' >/dev/null 2>&1 ; then
- echo "Error: this host '$(hostname)' has the primary IP '{{DOMSERVER_IP}}'."
+if ip addr | grep '{{ DOMSERVER_IP }}' >/dev/null 2>&1 ; then
+ echo "Error: this host '$(hostname)' has the primary IP '{{ DOMSERVER_IP }}'."
usage
exit 1
fi
@@ -96,9 +126,9 @@ get_field()
check_status()
{
local sql_info="$(printf '%s\n' "$@")"
- [ $(get_field 'Seconds_Behind_Master' "$sql_info") = '0' ] || return 1
- [ $(get_field 'Slave_SQL_Running' "$sql_info") = 'Yes' ] || return 1
- [ $(get_field 'Last_SQL_Errno' "$sql_info") = '0' ] || return 1
+ [ "$(get_field 'Seconds_Behind_Master' "$sql_info")" = '0' ] || return 1
+ [ "$(get_field 'Slave_SQL_Running' "$sql_info")" = 'Yes' ] || return 1
+ [ "$(get_field 'Last_SQL_Errno' "$sql_info")" = '0' ] || return 1
return 0
}
@@ -117,10 +147,12 @@ if check_replication ; then
fi
echo "Stopping PHP locally..."
-service php7.4-fpm stop
+service php{{ php_version.stdout }}-fpm stop
-echo "Stopping replication on $PRIMARY..."
-ssh "$PRIMARY" 'mysql -e "STOP SLAVE;"'
+if [ "$SETUP_MASTER_MASTER" -eq 1 ]; then
+ echo "Stopping replication on $PRIMARY..."
+ ssh "$PRIMARY" 'mysql -e "STOP SLAVE;"'
+fi
PRIMARY_STATUS=$(ssh "$PRIMARY" "mysql -e 'SHOW MASTER STATUS\G'")
PRIMARY_BINLOG=$(get_field 'File' "$PRIMARY_STATUS")
@@ -149,9 +181,9 @@ service mysql start
echo "Starting replication locally..."
mysql -e "
-CHANGE MASTER TO MASTER_HOST='{{SERVER_IP_PREFIX}}.${PRIMARY_SERVER_ID}',
+CHANGE MASTER TO MASTER_HOST='${PRIMARY}',
MASTER_USER='replication',
- MASTER_PASSWORD='{{REPLICATION_PASSWORD}}',
+ MASTER_PASSWORD='{{ REPLICATION_PASSWORD }}',
MASTER_LOG_FILE='${PRIMARY_BINLOG}',
MASTER_LOG_POS=${PRIMARY_LOGPOS};
START SLAVE;"
@@ -182,17 +214,19 @@ OUR_STATUS=$(mysql -e 'SHOW MASTER STATUS\G')
OUR_BINLOG=$(get_field 'File' "$OUR_STATUS")
OUR_LOGPOS=$(get_field 'Position' "$OUR_STATUS")
-echo "Starting replication on $PRIMARY..."
-ssh "$PRIMARY" "mysql -e '
-CHANGE MASTER TO MASTER_HOST=\"{{SERVER_IP_PREFIX}}.${OUR_SERVER_ID}\",
- MASTER_USER=\"replication\",
- MASTER_PASSWORD=\"{{REPLICATION_PASSWORD}}\",
- MASTER_LOG_FILE=\"${OUR_BINLOG}\",
- MASTER_LOG_POS=${OUR_LOGPOS};
-START SLAVE;'"
+if [ "$SETUP_MASTER_MASTER" -eq 1 ]; then
+ echo "Starting replication on $PRIMARY..."
+ ssh "$PRIMARY" "mysql -e '
+ CHANGE MASTER TO MASTER_HOST=\"{{ replication_interface_ip_address }}\",
+ MASTER_USER=\"replication\",
+ MASTER_PASSWORD=\"{{ REPLICATION_PASSWORD }}\",
+ MASTER_LOG_FILE=\"${OUR_BINLOG}\",
+ MASTER_LOG_POS=${OUR_LOGPOS};
+ START SLAVE;'"
+fi
echo "Restarting PHP locally..."
-service php7.4-fpm start
+service php{{ php_version.stdout }}-fpm start
if check_replication ; then
echo "Replication setup done."
diff --git a/provision-contest/ansible/roles/mysql_server/files/dump-db b/provision-contest/ansible/roles/mysql_server/files/dump-db
index 0a8adee3..9fa7bcd2 100644
--- a/provision-contest/ansible/roles/mysql_server/files/dump-db
+++ b/provision-contest/ansible/roles/mysql_server/files/dump-db
@@ -1,5 +1,9 @@
#!/bin/sh
+create_database_dump () {
+ sudo mysqldump --opt --skip-lock-tables domjudge | pv | gzip > "/home/domjudge/db-dumps/${1}.sql.gz"
+}
+
if [ -z "$1" ]
then
echo "Usage dump-db [name]"
@@ -7,4 +11,13 @@ then
exit 1
fi
-sudo mysqldump --opt --skip-lock-tables domjudge | pv | gzip > "/home/domjudge/db-dumps/${1}.sql.gz"
+if [ -f "/home/domjudge/db-dumps/${1}.sql.gz" ]; then
+ while true; do
+ read -p "Overwrite existing database dump (y/N)? " yn
+ case $yn in
+ [Yy]* ) break ;;
+ ''|[Nn]* ) exit 0;;
+ esac
+ done
+fi
+create_database_dump $1
diff --git a/provision-contest/ansible/roles/mysql_server/handlers/main.yml b/provision-contest/ansible/roles/mysql_server/handlers/main.yml
index 5ef1bb20..a3e1bb67 100644
--- a/provision-contest/ansible/roles/mysql_server/handlers/main.yml
+++ b/provision-contest/ansible/roles/mysql_server/handlers/main.yml
@@ -1,5 +1,6 @@
---
-# Define here handlers associated to this role.
-
-- name: restart mysql
- service: name=mysql enabled=true state=restarted
+- name: Restart mysql
+ service:
+ name: mysql
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/mysql_server/tasks/main.yml b/provision-contest/ansible/roles/mysql_server/tasks/main.yml
index 26479b04..0e9a6a9a 100644
--- a/provision-contest/ansible/roles/mysql_server/tasks/main.yml
+++ b/provision-contest/ansible/roles/mysql_server/tasks/main.yml
@@ -1,7 +1,7 @@
---
# These tasks install and configure a MySQL server
-- name: install mysql packages
+- name: Install mysql packages
apt:
state: present
pkg:
@@ -9,7 +9,7 @@
- python3-mysqldb
- mycli
-- name: copy in MySQL config
+- name: Copy in MySQL config
copy:
src: my.cnf
dest: /root/.my.cnf
@@ -17,7 +17,7 @@
owner: root
group: root
-- name: create directory for systemd mysql settings
+- name: Create directory for systemd mysql settings
file:
path: /etc/systemd/system/mysql.service.d/
state: directory
@@ -25,28 +25,33 @@
owner: root
mode: 0755
-- name: update systemd so mysql has bigger limits
+- name: Update systemd so mysql has bigger limits
copy:
src: mysql.override.cnf
dest: /etc/systemd/system/mysql.service.d/override.conf
mode: 0644
group: root
owner: root
- notify: restart mysql
+ notify: Restart mysql
-- name: add mysql config snippet to increase limits
+- name: Add mysql config snippet to increase limits
copy:
src: mysql.domjudge.cnf
dest: /etc/mysql/mariadb.conf.d/zz_domjudge.cnf
mode: 0644
owner: root
group: root
- notify: restart mysql
+ notify: Restart mysql
-- name: make sure mysql is restarted
+- name: Make sure mysql is restarted
meta: flush_handlers
-- name: create directory to store scripts & database dumps
+- name: Make sure mysql runs
+ service:
+ name: mysql
+ state: started
+
+- name: Create directory to store scripts & database dumps
file:
path: /home/domjudge/{{ item }}
owner: domjudge
@@ -57,7 +62,7 @@
- db-dumps
- bin
-- name: copy database dump/load scripts
+- name: Copy database dump/load scripts
copy:
src: "{{ item }}"
dest: /home/domjudge/bin/{{ item }}
diff --git a/provision-contest/ansible/roles/phpstorm/files/.idea/domjudge-checkout.iml b/provision-contest/ansible/roles/phpstorm/files/.idea/domjudge-checkout.iml
index 74663487..330bd4c2 100644
--- a/provision-contest/ansible/roles/phpstorm/files/.idea/domjudge-checkout.iml
+++ b/provision-contest/ansible/roles/phpstorm/files/.idea/domjudge-checkout.iml
@@ -178,6 +178,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/.idea/php.xml b/provision-contest/ansible/roles/phpstorm/files/.idea/php.xml
index beca0f3f..9d627638 100644
--- a/provision-contest/ansible/roles/phpstorm/files/.idea/php.xml
+++ b/provision-contest/ansible/roles/phpstorm/files/.idea/php.xml
@@ -179,9 +179,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/README.md b/provision-contest/ansible/roles/phpstorm/files/README.md
index 389c3816..8c394418 100644
--- a/provision-contest/ansible/roles/phpstorm/files/README.md
+++ b/provision-contest/ansible/roles/phpstorm/files/README.md
@@ -1,12 +1,12 @@
Place in this directory:
-* The unpacked PHPStorm .tar.gz file. Make sure the full version is correct set in variables.yml.
+* The unpacked PHPStorm .tar.gz file. Make sure the full version is correct set in group_vars/all/all.yml.
Download the .tar.gz from jetbrains.com.
-* The PHPStorm configuration directory containing our configuration, as PhpStorm20xx.y.
- Make sure the version is correctly set in variables.yml.
+* The PHPStorm configuration directory containing our configuration from `.config/JetBrains/PhpStorm20xx.y`, as PhpStorm20xx.y.
+ Make sure the version is correctly set in group_vars/all/all.yml.
Create the config by launching PHPStorm once on a machine and going through the wizard.
Also install the plugins we use. Download the latest version of the following plugins and set them up:
- https://plugins.jetbrains.com/plugin/8133-php-toolbox/versions
- https://plugins.jetbrains.com/plugin/7320-php-annotations/versions
- https://plugins.jetbrains.com/plugin/7219-symfony-support/versions
- https://plugins.jetbrains.com/plugin/7499-gittoolbox/versions
-* The PHPStorm local share directory containing our configuration, as PhpStorm20xx.y-local-share.
\ No newline at end of file
+* The PHPStorm local share directory containing our configuration from `.local/share/JetBrains/PhpStorm20xx.y`, as PhpStorm20xx.y-local-share.
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/_remote.repositories b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/_remote.repositories
new file mode 100644
index 00000000..e910e8fd
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/_remote.repositories
@@ -0,0 +1,3 @@
+#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.
+#Mon Oct 31 17:32:58 BDT 2022
+junit-bom-5.8.2.pom>central=
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom
new file mode 100644
index 00000000..c92aae6a
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+ 4.0.0
+ org.junit
+ junit-bom
+ 5.8.2
+ pom
+ JUnit 5 (Bill of Materials)
+ This Bill of Materials POM can be used to ease dependency management when referencing multiple JUnit artifacts using Gradle or Maven.
+ https://junit.org/junit5/
+
+
+ Eclipse Public License v2.0
+ https://www.eclipse.org/legal/epl-v20.html
+
+
+
+
+ bechte
+ Stefan Bechtold
+ stefan.bechtold@me.com
+
+
+ jlink
+ Johannes Link
+ business@johanneslink.net
+
+
+ marcphilipp
+ Marc Philipp
+ mail@marcphilipp.de
+
+
+ mmerdes
+ Matthias Merdes
+ matthias.merdes@heidelpay.com
+
+
+ sbrannen
+ Sam Brannen
+ sam@sambrannen.com
+
+
+ sormuras
+ Christian Stein
+ sormuras@gmail.com
+
+
+ juliette-derancourt
+ Juliette de Rancourt
+ derancourt.juliette@gmail.com
+
+
+
+ scm:git:git://github.com/junit-team/junit5.git
+ scm:git:git://github.com/junit-team/junit5.git
+ https://github.com/junit-team/junit5
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.8.2
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.8.2
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.8.2
+
+
+ org.junit.jupiter
+ junit-jupiter-migrationsupport
+ 5.8.2
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.8.2
+
+
+ org.junit.platform
+ junit-platform-commons
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-console
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-engine
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-jfr
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-reporting
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-runner
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-suite
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-suite-api
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-suite-commons
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-suite-engine
+ 1.8.2
+
+
+ org.junit.platform
+ junit-platform-testkit
+ 1.8.2
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.8.2
+
+
+
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom.sha1 b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom.sha1
new file mode 100644
index 00000000..3e210302
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/junit/junit-bom/5.8.2/junit-bom-5.8.2.pom.sha1
@@ -0,0 +1 @@
+90eab8a5a400f15b8e1cb6e65af0ceb616f23bba
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/_remote.repositories b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/_remote.repositories
new file mode 100644
index 00000000..6fa130ef
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/_remote.repositories
@@ -0,0 +1,4 @@
+#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.
+#Mon Oct 31 17:32:59 BDT 2022
+mariadb-java-client-3.0.7.jar>central=
+mariadb-java-client-3.0.7.pom>central=
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar
new file mode 100644
index 00000000..58d75f34
Binary files /dev/null and b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar differ
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar.sha1 b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar.sha1
new file mode 100644
index 00000000..93f5f241
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.jar.sha1
@@ -0,0 +1 @@
+708456abf19170c3c80896826993eaa5f663ffa4
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom
new file mode 100644
index 00000000..3a6be68d
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom
@@ -0,0 +1,458 @@
+
+
+ 4.0.0
+ org.mariadb.jdbc
+ mariadb-java-client
+ jar
+ mariadb-java-client
+ 3.0.7
+ JDBC driver for MariaDB and MySQL
+ https://mariadb.com/kb/en/mariadb/about-mariadb-connector-j/
+
+
+ UTF-8
+ 1.34
+ 5.8.2
+ 1.5.0
+ 3.22.0
+ 6.0.0
+ 5.0.0
+ UTF-8
+ 1.3.0-alpha12
+ 0.8.7
+ 3.1.1
+ 8.0.29
+ 6.2.0
+
+
+
+
+ LGPL-2.1
+
+
+
+
+ mariadb.com
+ https://mariadb.com
+
+
+
+
+ mariadbJdbcDevelopers
+ mariadb jdbc developers
+ http://mariadb.org/
+
+
+
+
+ scm:git:git://github.com/mariadb-corporation/mariadb-connector-j.git
+ https://github.com/mariadb-corporation/mariadb-connector-j
+ scm:git:git@github.com:MariaDB/mariadb-connector-j.git
+
+
+
+
+ JIRA
+ https://mariadb.atlassian.net/browse/CONJ
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
+
+
+
+
+ org.junit
+ junit-bom
+ ${junit.version}
+ pom
+ import
+
+
+
+ software.amazon.awssdk
+ bom
+ 2.17.24
+ pom
+ import
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ true
+
+ -Xlint:all,-options,-path,-processing
+
+ 1.8
+ 1.8
+
+
+
+ compile-java-8
+
+ compile
+
+
+
+ compile-java-9
+ compile
+
+ compile
+
+
+ 9
+ 9
+ 9
+
+ ${project.basedir}/src/main/java9
+
+ true
+
+
+
+ compile-java-11
+ compile
+
+ compile
+
+
+ 11
+ 11
+ 11
+
+ ${project.basedir}/src/main/java11
+
+ true
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ 1.8
+
+ org.mariadb.jdbc.client,org.mariadb.jdbc.codec,org.mariadb.jdbc.message,org.mariadb.jdbc.util,org.mariadb.jdbc.plugin.authentication.standard.ed25519,org.mariadb.jdbc.plugin.authentication.standard.ed25519.*
+
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+
+ true
+ org.mariadb.jdbc
+
+ javax.naming,javax.naming.ldap,javax.management,javax.sql,javax.net;resolution:=optional,javax.net.ssl;resolution:=optional,javax.transaction.xa;resolution:=optional,waffle.windows.auth;resolution:=optional,waffle.windows.auth.impl;resolution:=optional,org.ietf.jgss;resolution:=optional,javax.security.auth.login;resolution:=optional,javax.security.auth.x500;resolution:=optional,software.amazon.awssdk.*;resolution:=optional,org.slf4j;resolution:=optional
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.1
+
+
+
+ jar
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+
+ deploy
+
+
+
+ ossrh
+ https://oss.sonatype.org/
+ false
+ 15
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ verify
+
+ sign
+
+
+
+
+
+
+ com.coveo
+ fmt-maven-plugin
+ 2.9.1
+
+
+
+ format
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ ${bnd-maven-plugin.version}
+
+ bnd.bnd
+
+
+
+
+ bnd-process
+
+
+
+
+
+
+
+
+
+
+
+ com.github.waffle
+ waffle-jna
+ ${waffle-jna.version}
+ true
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+ org.junit-pioneer
+ junit-pioneer
+ ${junit-pioneer.version}
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.0-alpha5
+ true
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ test
+
+
+
+ org.osgi
+ org.osgi.core
+ ${osgi.version}
+ provided
+
+
+
+ org.osgi
+ org.osgi.compendium
+ ${osgi.compendium.version}
+ provided
+
+
+
+ software.amazon.awssdk
+ rds
+ true
+
+
+
+
+
+
+
+ default
+
+ true
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ jacoco-initialize
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+
+
+
+
+
+
+
+ bench
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+
+
+
+ mysql
+ mysql-connector-java
+ ${mysql-connector-java.version}
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ src/benchmark/java
+
+
+
+
+ add-resource
+ generate-resources
+
+ add-resource
+
+
+
+
+ src/benchmark/resources
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+ benchmarks
+
+
+ org.openjdk.jmh.Main
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom.sha1 b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom.sha1
new file mode 100644
index 00000000..2a985165
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/org/mariadb/jdbc/mariadb-java-client/3.0.7/mariadb-java-client-3.0.7.pom.sha1
@@ -0,0 +1 @@
+81f83a79a9d7626e800ee1d7f949f44934b7315c
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/_remote.repositories b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/_remote.repositories
new file mode 100644
index 00000000..29095cd2
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/_remote.repositories
@@ -0,0 +1,3 @@
+#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.
+#Mon Oct 31 17:32:58 BDT 2022
+aws-sdk-java-pom-2.17.24.pom>central=
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom
new file mode 100644
index 00000000..8c271b2b
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom
@@ -0,0 +1,1200 @@
+
+
+
+
+ 4.0.0
+ software.amazon.awssdk
+ aws-sdk-java-pom
+ 2.17.24
+ pom
+ AWS Java SDK :: Parent
+ The Amazon Web Services SDK for Java provides Java APIs
+ for building software on AWS' cost-effective, scalable, and reliable
+ infrastructure products. The AWS Java SDK allows developers to code
+ against APIs for all of Amazon's infrastructure web services (Amazon
+ S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon
+ AutoScaling, etc).
+
+ https://aws.amazon.com/sdkforjava
+
+
+ Apache License, Version 2.0
+ https://aws.amazon.com/apache2.0
+ repo
+
+
+
+
+ amazonwebservices
+ Amazon Web Services
+ https://aws.amazon.com
+
+ developer
+
+
+
+
+
+ aws-sdk-java
+ core
+ services
+ services-custom
+ bom
+ bom-internal
+ codegen
+ http-client-spi
+ http-clients
+ codegen-maven-plugin
+ bundle
+ build-tools
+ metric-publishers
+ release-scripts
+ utils
+ codegen-lite
+ codegen-lite-maven-plugin
+ archetypes
+ third-party
+ test/http-client-tests
+ test/protocol-tests
+ test/protocol-tests-core
+ test/service-test-utils
+ test/test-utils
+ test/codegen-generated-classes-test
+ test/sdk-benchmarks
+ test/module-path-tests
+ test/tests-coverage-reporting
+ test/stability-tests
+ test/sdk-native-image-test
+ test/s3-benchmarks
+ test/auth-sts-testing
+
+
+ https://github.com/aws/aws-sdk-java-v2.git
+
+
+ ${project.version}
+ 2.17.23
+ 2.12.3
+ 2.12.3
+ 2.12.3
+ 1.0.1
+ 3.12.0
+ 2.18.0
+ 1.7.30
+ 1.2.17
+ 2.5
+ 3.5
+
+
+ 4.1.66.Final
+ 3.4.6
+ 1.3
+ UTF-8
+ 4.1.4
+ 2.0.5
+ 1.13.0
+ 3.10.0
+ 3.5.101
+ 2.2.21
+ 1.10
+ 1.29
+ 0.13.14
+
+
+ 4.13.1
+ 5.7.1
+ 1.3
+ 1.10.19
+ 2.28.2
+ 3.8.0
+ 29.0-jre
+ 1.1
+ 7.1.0
+ 2.6
+ 2.0.40.Final
+ 1.16.0
+ 1.0.392
+
+
+ 2.22.2
+ 3.8.1
+ 3.1.2
+ 2.22.2
+ 3.1.1
+ 3.0.1
+ yyyy
+ 3.1.1
+ 1.6
+ 8.42
+ 0.8.5
+ 1.6.8
+ 1.6.0
+ 2.8.2
+ 3.0.0
+ 0.14.4
+
+
+ 2.4.0
+
+
+ 3.0.7.RELEASE
+ 2.3.9
+ 1.8.2
+
+ 1.8
+ 4.5.13
+ 4.4.11
+
+
+ 1.0.3
+
+ ${skipTests}
+
+
+
+
+
+
+
+
+
+
+
+ aws-sdk-java-${project.artifactId}-${awsjavasdk.version}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${jre.version}
+ ${jre.version}
+ UTF-8
+ true
+ -proc:none
+ false
+ -proc:none
+
+ software/amazon/awssdk/services/kinesis/model/SubscribeToShardEvent.java
+ software/amazon/awssdk/services/kinesis/KinesisAsyncClient.java
+ software/amazon/awssdk/services/kinesis/DefaultKinesisAsyncClient.java
+ software/amazon/awssdk/services/kinesis/DefaultKinesisBaseClientBuilder.java
+ software/amazon/awssdk/services/sms/model/InternalError.java
+ software/amazon/awssdk/services/kinesis/transform/SubscribeToShardResponseUnmarshaller.java
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ ${maven-dependency-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.version}
+
+ ${argLine}
+
+ **/*StabilityTest.java
+ **/*StabilityTests.java
+ **/*CucumberTest.java
+ **/*IntegrationTest.java
+ **/*IntegrationTests.java
+ **/*IntegTest.java
+ **/*IntegrationTestCase.java
+
+
+ **/Test*.java
+ **/*Tests.java
+ **/*Test.java
+ **/*TestCase.java
+
+ ${skip.unit.tests}
+
+
+
+
+ org.apache.maven.surefire
+ surefire-junit47
+ ${maven.surefire.version}
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${build-helper-maven-plugin.version}
+
+
+ add-integ-sources
+ generate-sources
+
+ add-test-source
+
+
+
+ ${basedir}/src/it/java
+
+
+
+
+ add-integ-resources
+ generate-sources
+
+ add-test-resource
+
+
+
+
+ ${basedir}/src/it/resources
+
+
+
+
+
+ add-generated-sources
+ generate-sources
+
+ add-source
+
+
+
+ ${basedir}/target/generated-sources/sdk
+
+
+
+
+ add-license-notice
+ generate-sources
+
+ add-resource
+
+
+
+
+ ${maven.multiModuleProjectDirectory}
+
+ LICENSE.txt
+ NOTICE.txt
+
+ META-INF
+
+
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.version}
+
+
+ software.amazon.awssdk
+ build-tools
+ 1.0
+
+
+
+
+ findbugs
+ process-classes
+
+ check
+
+
+
+
+ true
+ false
+ true
+ software/amazon/awssdk/spotbugs-suppressions.xml
+ Low
+ Max
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${maven-checkstyle-plugin.version}
+
+
+ com.puppycrawl.tools
+ checkstyle
+ ${checkstyle.version}
+
+
+ software.amazon.awssdk
+ build-tools
+ 1.0
+
+
+
+
+ checkstyle
+ validate
+
+ check
+
+
+
+
+ software/amazon/awssdk/checkstyle.xml
+ software/amazon/awssdk/checkstyle-suppressions.xml
+ true
+ true
+ true
+ true
+ **/module-info.java
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${build-helper-maven-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ ${maven-dependency-plugin.version}
+
+
+
+ analyze-only
+
+
+
+
+ true
+ true
+
+
+ junit:*:*
+ software.amazon.awssdk:test-utils
+ org.hamcrest:*
+ org.testng:testng
+ org.mockito:*
+ org.junit.platform:*
+ org.opentest4j:*
+
+
+ org.reactivestreams:reactive-streams
+ com.fasterxml.jackson.core:*
+ org.slf4j:slf4j-api
+
+
+
+ com.typesafe.netty:*
+ software.amazon.awssdk:aws-sdk-java
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${maven-checkstyle-plugin.version}
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ jacoco-site
+ package
+
+ report
+
+
+
+
+ pre-integration-test
+ integration-test
+
+ prepare-agent
+
+
+
+ post-integration-test
+ post-integration-test
+
+ report
+
+
+
+
+
+ software/amazon/awssdk/modulepath/tests/**/*.class
+ software/amazon/awssdk/nativeimagetest/**/*.class
+ software/amazon/awssdk/testutils/service/**/*.class
+ **/*Benchmark.class
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.version}
+
+
+
+
+ com.github.siom79.japicmp
+ japicmp-maven-plugin
+ ${japicmp-maven-plugin.version}
+
+
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${awsjavasdk.previous.version}
+ jar
+
+
+
+
+ ${project.build.directory}/aws-sdk-java-${project.artifactId}-${project.version}.${project.packaging}
+
+
+
+ true
+
+ *.internal.*
+
+
+
+ codegen-lite-maven-plugin
+ codegen-maven-plugin
+ codegen
+ codegen-lite
+ .*tests*
+ .*test*
+ protocol-tests-core
+ tests-coverage-reporting
+ aws-sdk-java
+ archetype-lambda
+ archetype-app-quickstart
+ archetype-tools
+ sdk-benchmarks
+ bundle
+ s3-benchmarks
+
+ aws-crt-client
+ s3-transfer-manager
+
+ true
+ true
+ true
+
+
+ METHOD_NEW_DEFAULT
+ true
+ true
+
+
+ METHOD_ADDED_TO_INTERFACE
+ true
+ true
+
+
+
+
+
+
+ verify
+
+ cmp
+
+
+
+
+
+
+
+
+
+
+ sonar-generate
+
+ true
+ true
+ true
+ true
+
+
+
+
+ publishing
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven-gpg-plugin.version}
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ ${nexus-staging-maven-plugin.version}
+ true
+
+ sonatype-nexus-staging
+ https://aws.oss.sonatype.org
+
+
+
+
+
+
+
+ disable-java8-doclint
+
+ [1.8,)
+
+
+ -Xdoclint:none
+
+
+
+
+ quick
+
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+ integration-tests
+
+
+ doRelease
+
+
+
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ integration-test
+
+ integration-test
+ verify
+
+
+
+ ${argLine}
+
+ **/*IntegrationTest.java
+ **/*IntegrationTests.java
+ **/*IntegTest.java
+ **/RunCucumberTest.java
+
+
+ **/SimpleMethodsIntegrationTest.java
+
+ false
+ 2
+
+
+
+
+
+
+
+
+
+ stability-tests
+
+
+ doRelease
+
+
+
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ integration-test
+
+ integration-test
+ verify
+
+
+ -Dio.netty.leakDetection.level=advanced
+
+ **/*StabilityTest.java
+ **/*StabilityTests.java
+
+ false
+ 2
+
+
+
+
+
+
+
+
+
+ simple-method-integration-tests
+
+
+ doRelease
+
+
+
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ integration-test
+
+ integration-test
+ verify
+
+
+
+ **/SimpleMethodsIntegrationTest.java
+
+ false
+ 2
+
+
+
+
+
+
+
+
+
+ public-javadoc
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+ 128m
+ 8g
+ false
+ public
+ false
+ true
+ false
+ true
+ true
+
+ --allow-script-in-comments
+
+ AWS SDK for Java - ${awsjavasdk.version}
+ UTF-8
+ UTF-8
+ AWS SDK for Java API Reference - ${awsjavasdk.version}
+ AWS SDK for Java
+ :*.codegen:software.amazon.awssdk.services.protocol*
+
+
+ Greengrass
+ software.amazon.awssdk.services.greengrass*
+
+
+ Athena
+ software.amazon.awssdk.services.athena*
+
+
+ Marketplace Entitlement
+ software.amazon.awssdk.services.marketplaceentitlement*
+
+
+ CodeStar
+ software.amazon.awssdk.services.codestar*
+
+
+ Lex Model Building
+ software.amazon.awssdk.services.lexmodelbuilding*
+
+
+ Resource Groups Tagging API
+ software.amazon.awssdk.services.resourcegroupstaggingapi*
+
+
+ S3 Control
+ software.amazon.awssdk.services.s3control*
+
+
+ S3
+ software.amazon.awssdk.services.s3*
+
+
+ Glacier
+ software.amazon.awssdk.services.glacier*
+
+
+ DynamoDB
+ software.amazon.awssdk.services.dynamo*
+
+
+ EC2
+ software.amazon.awssdk.services.ec2*
+
+
+ SQS
+ software.amazon.awssdk.services.sqs*
+
+
+ SNS
+ software.amazon.awssdk.services.sns*
+
+
+ Relational Database Service
+ software.amazon.awssdk.services.rds*
+
+
+ Route 53
+ software.amazon.awssdk.services.route53*
+
+
+ Simple Workflow Service
+
+ software.amazon.awssdk.services.swf*:software.amazon.awssdk.services.simpleworkflow*
+
+
+ Elastic MapReduce
+ software.amazon.awssdk.services.emr*
+
+
+ Simple Email Service
+
+ software.amazon.awssdk.services.ses*:software.amazon.awssdk.services.simpleemail*
+
+
+ Elastic Load Balancing
+ software.amazon.awssdk.services.elasticloadbalancing*
+
+
+ CloudSearch
+ software.amazon.awssdk.services.cloudsearch*
+
+
+ CloudWatch
+ software.amazon.awssdk.services.cloudwatch*
+
+
+ CloudWatch Logs
+ software.amazon.awssdk.services.logs*
+
+
+ CloudWatch Events
+ software.amazon.awssdk.services.cloudwatchevents*
+
+
+ CloudFront
+ software.amazon.awssdk.services.cloudfront*
+
+
+ CloudDirectory
+ software.amazon.awssdk.services.clouddirectory*
+
+
+ Cognito
+ software.amazon.awssdk.services.cognito*
+
+
+ AutoScaling
+ software.amazon.awssdk.services.autoscaling*
+
+
+ Kinesis
+ software.amazon.awssdk.services.kinesis*:software.amazon.awssdk.services.firehose*
+
+
+ Redshift
+ software.amazon.awssdk.services.redshift*
+
+
+ ElastiCache
+ software.amazon.awssdk.services.elasticache*
+
+
+ Elastic Transcoder
+ software.amazon.awssdk.services.elastictranscoder*
+
+
+ OpsWorks
+ software.amazon.awssdk.services.opsworks*
+
+
+ CloudFormation
+ software.amazon.awssdk.services.cloudformation*
+
+
+ Data Pipeline
+ software.amazon.awssdk.services.datapipeline*
+
+
+ Direct Connect
+ software.amazon.awssdk.services.directconnect*
+
+
+ Elastic Beanstalk
+ software.amazon.awssdk.services.elasticbeanstalk*
+
+
+ Identity and Access Management
+ software.amazon.awssdk.services.iam*
+
+
+ Security Token Service
+ software.amazon.awssdk.services.sts*
+
+
+ Storage Gateway Service
+ software.amazon.awssdk.services.storagegateway*
+
+
+ Support
+ software.amazon.awssdk.services.support*
+
+
+ Transcribe Streaming
+ software.amazon.awssdk.services.transcribestreaming*
+
+
+ CloudTrail
+ software.amazon.awssdk.services.cloudtrail*
+
+
+ Config
+ software.amazon.awssdk.services.config*
+
+
+ Certificate Manager
+ software.amazon.awssdk.services.acm*
+
+
+ Key Management
+ software.amazon.awssdk.services.kms*
+
+
+ Lambda
+ software.amazon.awssdk.services.lambda*
+
+
+ EC2 Container Service
+ software.amazon.awssdk.services.ecs*
+
+
+ CloudHSM
+ software.amazon.awssdk.services.cloudhsm*
+
+
+ Simple Systems Management Service
+ software.amazon.awssdk.services.ssm*
+
+
+ WorkSpaces
+ software.amazon.awssdk.services.workspaces*
+
+
+ Machine Learning
+ software.amazon.awssdk.services.machinelearning*
+
+
+ Directory Service
+ software.amazon.awssdk.services.directory*
+
+
+ Elastic File System
+ software.amazon.awssdk.services.efs*
+
+
+ CodePipeline
+ software.amazon.awssdk.services.codepipeline*
+
+
+ CodeCommit
+ software.amazon.awssdk.services.codecommit*
+
+
+ Device Farm
+ software.amazon.awssdk.services.devicefarm*
+
+
+ Elasticsearch Service
+ software.amazon.awssdk.services.elasticsearch*
+
+
+ Marketplace Commerce Analytics
+ software.amazon.awssdk.services.marketplacecommerceanalytics*
+
+
+ WAF
+ software.amazon.awssdk.services.waf*
+
+
+ Inspector Service
+ software.amazon.awssdk.services.inspector*
+
+
+ IoT
+ software.amazon.awssdk.services.iot*
+
+
+ API Gateway
+ software.amazon.awssdk.services.apigateway*
+
+
+ EC2 Container Registry
+ software.amazon.awssdk.services.ecr*
+
+
+ GameLift
+ software.amazon.awssdk.services.gamelift*
+
+
+ Database Migration Service
+ software.amazon.awssdk.services.databasemigration*
+
+
+ Marketplace Metering Service
+ software.amazon.awssdk.services.marketplacemetering*
+
+
+ Cognito Identity Provider
+ software.amazon.awssdk.services.cognitoidp*
+
+
+ Application Discovery Service
+ software.amazon.awssdk.services.applicationdiscovery*
+
+
+ Application Auto Scaling
+ software.amazon.awssdk.services.applicationautoscaling*
+
+
+ Snowball
+ software.amazon.awssdk.services.snowball*
+
+
+ Service Catalog
+ software.amazon.awssdk.services.servicecatalog*
+
+
+ Budgets
+ software.amazon.awssdk.services.budgets*
+
+
+ Server Migration
+ software.amazon.awssdk.services.sms*
+
+
+ Rekognition
+ software.amazon.awssdk.services.rekognition*
+
+
+ Polly
+ software.amazon.awssdk.services.polly*
+
+
+ Lightsail
+ software.amazon.awssdk.services.lightsail*
+
+
+ AppStream
+ software.amazon.awssdk.services.appstream*
+
+
+ X-Ray
+ software.amazon.awssdk.services.xray*
+
+
+ OpsWorks for Chef Automate
+ software.amazon.awssdk.services.opsworkscm*
+
+
+ Pinpoint
+ software.amazon.awssdk.services.pinpoint*
+
+
+ Step Functions
+
+ software.amazon.awssdk.services.sfn*:software.amazon.awssdk.services.stepfunctions*
+
+
+ Shield
+ software.amazon.awssdk.services.shield*
+
+
+ Health APIs and Notifications
+ software.amazon.awssdk.services.health*
+
+
+ Cost and Usage Report
+ software.amazon.awssdk.services.costandusagereport*
+
+
+ Code Build
+ software.amazon.awssdk.services.codebuild*
+
+
+ Batch
+ software.amazon.awssdk.services.batch*
+
+
+ Lex Runtime
+ software.amazon.awssdk.services.lexruntime*
+
+
+ Lex Model Building
+ software.amazon.awssdk.services.lexmodelbuilding*
+
+
+ Mechanical Turk Requester
+ software.amazon.awssdk.services.mechanicalturkrequester*
+
+
+ Organizations
+ software.amazon.awssdk.services.organizations*
+
+
+ WorkDocs
+ software.amazon.awssdk.services.workdocs*
+
+
+ CodeDeploy
+ software.amazon.awssdk.services.codedeploy*
+
+
+ Common
+ software.amazon.awssdk*
+
+
+ AWS SDK for Java API Reference - ${project.version}]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom.sha1 b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom.sha1
new file mode 100644
index 00000000..48f1db16
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/aws-sdk-java-pom/2.17.24/aws-sdk-java-pom-2.17.24.pom.sha1
@@ -0,0 +1 @@
+1888d356c8d5904ea30964d1b2e12b472847ad68
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/_remote.repositories b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/_remote.repositories
new file mode 100644
index 00000000..000b0763
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/_remote.repositories
@@ -0,0 +1,3 @@
+#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.
+#Mon Oct 31 17:32:58 BDT 2022
+bom-2.17.24.pom>central=
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom
new file mode 100644
index 00000000..a9330e00
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom
@@ -0,0 +1,1543 @@
+
+
+ 4.0.0
+
+ software.amazon.awssdk
+ aws-sdk-java-pom
+ 2.17.24
+ ../pom.xml
+
+ bom
+ pom
+ AWS Java SDK :: Bill of Materials
+ The AWS SDK for Java - BOM module holds the dependency managements for individual java clients.
+ https://aws.amazon.com/sdkforjava
+
+ true
+
+
+
+
+
+ software.amazon.awssdk
+ codegen-lite-maven-plugin
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegen-lite
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegen-maven-plugin
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegen
+ ${awsjavasdk.version}
+
+
+
+ software.amazon.awssdk
+ annotations
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ arns
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ json-utils
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ third-party-jackson-core
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ third-party-jackson-dataformat-cbor
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ auth
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ aws-core
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ profiles
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ aws-cbor-protocol
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ aws-json-protocol
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ aws-query-protocol
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ aws-xml-protocol
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ protocol-core
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ regions
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sdk-core
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ http-client-spi
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ apache-client
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ netty-nio-client
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ url-connection-client
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ utils
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudwatch-metric-publisher
+ ${awsjavasdk.version}
+
+
+
+ software.amazon.awssdk
+ acm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ acmpca
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ alexaforbusiness
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ amplify
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ apigateway
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ apigatewaymanagementapi
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ apigatewayv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ applicationautoscaling
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ applicationdiscovery
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appmesh
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appstream
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appsync
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ athena
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ autoscaling
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ autoscalingplans
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ backup
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ batch
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ budgets
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ chime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloud9
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ clouddirectory
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudformation
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudfront
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudhsm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudhsmv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudsearch
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudsearchdomain
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudtrail
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudwatch
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudwatchevents
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cloudwatchlogs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codebuild
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codecommit
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codedeploy
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codepipeline
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codestar
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cognitoidentity
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cognitoidentityprovider
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ cognitosync
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ comprehend
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ comprehendmedical
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ config
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ connect
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ costandusagereport
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ costexplorer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ databasemigration
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ datapipeline
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ datasync
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ dax
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ devicefarm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ directconnect
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ directory
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ dlm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ docdb
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ dynamodb
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ dynamodb-enhanced
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ec2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ecr
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ecs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ efs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ eks
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticache
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticbeanstalk
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticloadbalancing
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticloadbalancingv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticsearch
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elastictranscoder
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ emr
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ firehose
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ fms
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ fsx
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ gamelift
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ glacier
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ globalaccelerator
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ glue
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ greengrass
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ guardduty
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ health
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iam
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ inspector
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iot
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iot1clickdevices
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iot1clickprojects
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotanalytics
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotdataplane
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotjobsdataplane
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kafka
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesis
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisanalytics
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisanalyticsv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisvideo
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisvideoarchivedmedia
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisvideomedia
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kms
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lambda
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lexmodelbuilding
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lexruntime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ licensemanager
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lightsail
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ machinelearning
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ macie
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ managedblockchain
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ marketplacecommerceanalytics
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ marketplaceentitlement
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ marketplacemetering
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediaconnect
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediaconvert
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ medialive
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediapackage
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediapackagevod
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediastore
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediastoredata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mediatailor
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ migrationhub
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mobile
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mq
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mturk
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ neptune
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ opsworks
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ opsworkscm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ organizations
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ pi
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ pinpoint
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ pinpointemail
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ pinpointsmsvoice
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ polly
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ pricing
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ quicksight
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ram
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ rds
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ rdsdata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ redshift
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ rekognition
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ resourcegroups
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ resourcegroupstaggingapi
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ robomaker
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53domains
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53resolver
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ s3
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ s3control
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sagemaker
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sagemakerruntime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ secretsmanager
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ securityhub
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ serverlessapplicationrepository
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ servicecatalog
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ servicediscovery
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ses
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sfn
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ shield
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ signer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sms
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ snowball
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sns
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sqs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ssm
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ storagegateway
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sts
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ support
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ swf
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ textract
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ transcribe
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ transcribestreaming
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ transfer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ translate
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ waf
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ workdocs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ worklink
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ workmail
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ workspaces
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ xray
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ groundstation
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotthingsgraph
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotevents
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ioteventsdata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ personalizeruntime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ personalize
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ personalizeevents
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ servicequotas
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ applicationinsights
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ec2instanceconnect
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ eventbridge
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lakeformation
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ forecast
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ forecastquery
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ qldb
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ qldbsession
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ workmailmessageflow
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codestarnotifications
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ savingsplans
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sso
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ssooidc
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ marketplacecatalog
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sesv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ dataexchange
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ migrationhubconfig
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ connectparticipant
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ wafv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appconfig
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotsecuretunneling
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ elasticinference
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ imagebuilder
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ schemas
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ accessanalyzer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ computeoptimizer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ networkmanager
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kendra
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ frauddetector
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegurureviewer
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codeguruprofiler
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ outposts
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sagemakera2iruntime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ebs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ kinesisvideosignaling
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ detective
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codestarconnections
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ synthetics
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotsitewise
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ macie2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codeartifact
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ honeycode
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ivs
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ braket
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ identitystore
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appflow
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ redshiftdata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ssoadmin
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ timestreamwrite
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ timestreamquery
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ s3outposts
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ metrics-spi
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ databrew
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ servicecatalogappregistry
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ networkfirewall
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mwaa
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ devopsguru
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sagemakerfeaturestoreruntime
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ appintegrations
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ecrpublic
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ amplifybackend
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ connectcontactlens
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lookoutvision
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ customerprofiles
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ emrcontainers
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ sagemakeredge
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ healthlake
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ auditmanager
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ amp
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ greengrassv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotwireless
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotfleethub
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ iotdeviceadvisor
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ location
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ wellarchitected
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lexruntimev2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lexmodelsv2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ fis
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lookoutmetrics
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ mgn
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ lookoutequipment
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ nimble
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ finspacedata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ finspace
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ssmincidents
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ ssmcontacts
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ applicationcostprofiler
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ apprunner
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ proton
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53recoveryreadiness
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53recoverycontrolconfig
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ route53recoverycluster
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ chimesdkmessaging
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ chimesdkidentity
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ snowdevicemanagement
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ memorydb
+ ${awsjavasdk.version}
+
+
+
+
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom.sha1 b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom.sha1
new file mode 100644
index 00000000..9f4405d1
--- /dev/null
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/MariaDB Connector J/3.0.7/software/amazon/awssdk/bom/2.17.24/bom-2.17.24.pom.sha1
@@ -0,0 +1 @@
+27d428dcc8d658caa04d531b938cbfb5758d463c
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/jdbc-drivers.xml b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/jdbc-drivers.xml
index b9f79f8f..6250f17a 100644
--- a/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/jdbc-drivers.xml
+++ b/provision-contest/ansible/roles/phpstorm/files/jdbc-drivers/jdbc-drivers.xml
@@ -25,6 +25,10 @@
md5=""/>
+
+
+
+
@@ -101,7 +105,8 @@
-
+
+
@@ -133,6 +138,10 @@
+
+
+
+
@@ -187,6 +196,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -236,7 +269,8 @@
-
+
+
@@ -344,7 +378,21 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -387,6 +435,30 @@
md5=""/>
+
+
+
+
+
+
+
+
+
+
+
+
@@ -414,6 +486,11 @@
md5=""/>
+
+
+
+
+
@@ -511,6 +588,43 @@
url="https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/jdbc-drivers/MSSQL/9/sqlserver9-win-auth.jar" md5=""/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -623,6 +737,14 @@
url="https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/jdbc-drivers/Redshift/license2.txt"
md5=""/>
+
+
+
+
@@ -644,9 +766,17 @@
md5=""/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -772,124 +920,67 @@
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
+
-
- -
+
-
- -
+
-
- -
+
-
-
- -
+
-
-
- -
+
-
-
- -
+
-
@@ -910,6 +1001,14 @@
url="https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/jdbc-drivers/couchbase/license.txt"
md5=""/>
+
+
+
+
@@ -922,6 +1021,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -929,6 +1043,10 @@
+
+
+
+
@@ -974,6 +1092,10 @@
+
+
+
+
@@ -1037,6 +1159,12 @@
+
+
+
+
+
+
@@ -1078,5 +1206,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/provision-contest/ansible/roles/phpstorm/meta/main.yml b/provision-contest/ansible/roles/phpstorm/meta/main.yml
index 4a4f743a..a480e52b 100644
--- a/provision-contest/ansible/roles/phpstorm/meta/main.yml
+++ b/provision-contest/ansible/roles/phpstorm/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: domjudge_user
+ tags: domjudge_user
diff --git a/provision-contest/ansible/roles/phpstorm/tasks/main.yml b/provision-contest/ansible/roles/phpstorm/tasks/main.yml
index fb0b1f4e..9310d34b 100644
--- a/provision-contest/ansible/roles/phpstorm/tasks/main.yml
+++ b/provision-contest/ansible/roles/phpstorm/tasks/main.yml
@@ -22,7 +22,7 @@
owner: false
use_ssh_args: true
-- name: fix ownership of PHPStorm config
+- name: Fix ownership of PHPStorm config
file:
path: "/home/domjudge/.config/JetBrains/PhpStorm{{ PHPSTORM_VERSION }}"
recurse: true
@@ -45,7 +45,7 @@
recursive: true
use_ssh_args: true
-- name: fix ownership of PHPStorm local share
+- name: Fix ownership of PHPStorm local share
file:
path: "/home/domjudge/.local/share/JetBrains/PhpStorm{{ PHPSTORM_VERSION }}"
recurse: true
@@ -59,7 +59,7 @@
owner: false
use_ssh_args: true
-- name: fix ownership of PHPStorm config
+- name: Fix ownership of PHPStorm config
file:
path: "{{ DJ_DIR }}/.idea"
recurse: true
diff --git a/provision-contest/ansible/roles/phpstorm/templates/jetbrains-phpstorm.desktop.j2 b/provision-contest/ansible/roles/phpstorm/templates/jetbrains-phpstorm.desktop.j2
index 0c21c06e..a51cea95 100644
--- a/provision-contest/ansible/roles/phpstorm/templates/jetbrains-phpstorm.desktop.j2
+++ b/provision-contest/ansible/roles/phpstorm/templates/jetbrains-phpstorm.desktop.j2
@@ -2,8 +2,8 @@
Version=1.0
Type=Application
Name=PHPStorm
-Icon=/phpstorm/PhpStorm-{{ PHPSTORM_FULL_VERSION }}/bin/phpstorm.png
-Exec="/phpstorm/PhpStorm-{{ PHPSTORM_FULL_VERSION }}/bin/phpstorm.sh" %f
+Icon=/opt/phpstorm/PhpStorm-{{ PHPSTORM_FULL_VERSION }}/bin/phpstorm.png
+Exec="/opt/phpstorm/PhpStorm-{{ PHPSTORM_FULL_VERSION }}/bin/phpstorm.sh" %f
Comment=PHPStorm (UDTC)
Categories=Development;IDE;PHP
Terminal=false
diff --git a/provision-contest/ansible/roles/presadmin/files/presadmin.desktop b/provision-contest/ansible/roles/presadmin/files/presadmin.desktop
new file mode 100644
index 00000000..79451040
--- /dev/null
+++ b/provision-contest/ansible/roles/presadmin/files/presadmin.desktop
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Name=presadmin
+Type=Application
+Exec=/usr/local/bin/presadmin
diff --git a/provision-contest/ansible/roles/presadmin/tasks/main.yml b/provision-contest/ansible/roles/presadmin/tasks/main.yml
new file mode 100644
index 00000000..37f22bc9
--- /dev/null
+++ b/provision-contest/ansible/roles/presadmin/tasks/main.yml
@@ -0,0 +1,56 @@
+---
+# These tasks configure the presentation admin
+
+- name: Get the presentation admin release
+ uri:
+ url: https://api.github.com/repos/icpctools/icpctools/releases?per_page=1
+ method: GET
+ return_content: true
+ status_code: 200
+ body_format: json
+ register: latest_pres_admin_release_array
+ check_mode: no
+
+- name: Set pres admin latest version
+ set_fact:
+ pres_admin_version: "{{ latest_pres_admin_release_array.json[0].name | replace('v', '') }}"
+
+- name: Set pres admin minor version
+ set_fact:
+ pres_admin_version_minor: "{{ pres_admin_version | regex_replace('\\.\\d+$', '') }}"
+
+- name: Create presentation admin directory
+ file:
+ path: /home/domjudge/presadmin
+ state: directory
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+
+- name: Download and unpack presentation admin
+ unarchive:
+ src: https://github.com/icpctools/icpctools/releases/download/v{{ pres_admin_version }}/presentationAdmin-{{ pres_admin_version }}.zip
+ dest: /home/domjudge/presadmin
+ remote_src: true
+ owner: domjudge
+ group: domjudge
+
+- name: Find the presentation admin user
+ set_fact:
+ presentation_admin_account: "{{ CDS_ACCOUNTS | selectattr('username', '==', 'presAdmin') }}"
+
+- name: Add presentation admin script
+ template:
+ src: presadmin.j2
+ dest: /usr/local/bin/presadmin
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+
+- name: Add presentation admin autostart
+ copy:
+ src: presadmin.desktop
+ dest: /home/domjudge/.config/autostart/presadmin.desktop
+ owner: domjudge
+ group: domjudge
+ mode: 0755
diff --git a/provision-contest/ansible/roles/presadmin/templates/presadmin.j2 b/provision-contest/ansible/roles/presadmin/templates/presadmin.j2
new file mode 100644
index 00000000..2f5dcd40
--- /dev/null
+++ b/provision-contest/ansible/roles/presadmin/templates/presadmin.j2
@@ -0,0 +1 @@
+/home/domjudge/presadmin/presAdmin.sh https://{{ CDS_HOSTNAME }} {{ presentation_admin_account[0].username }} {{ presentation_admin_account[0].password }}
diff --git a/provision-contest/ansible/roles/presclient/files/presclient.desktop b/provision-contest/ansible/roles/presclient/files/presclient.desktop
new file mode 100644
index 00000000..19e75d04
--- /dev/null
+++ b/provision-contest/ansible/roles/presclient/files/presclient.desktop
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Name=presclient
+Type=Application
+Exec=/usr/local/bin/presclient
diff --git a/provision-contest/ansible/roles/presclient/tasks/main.yml b/provision-contest/ansible/roles/presclient/tasks/main.yml
new file mode 100644
index 00000000..1575a3d3
--- /dev/null
+++ b/provision-contest/ansible/roles/presclient/tasks/main.yml
@@ -0,0 +1,56 @@
+---
+# These tasks configure the presentation clients
+
+- name: Get the presentation client release
+ uri:
+ url: https://api.github.com/repos/icpctools/icpctools/releases?per_page=1
+ method: GET
+ return_content: true
+ status_code: 200
+ body_format: json
+ register: latest_pres_client_release_array
+ check_mode: no
+
+- name: Set pres client latest version
+ set_fact:
+ pres_client_version: "{{ latest_pres_client_release_array.json[0].name | replace('v', '') }}"
+
+- name: Set pres client minor version
+ set_fact:
+ pres_client_version_minor: "{{ pres_client_version | regex_replace('\\.\\d+$', '') }}"
+
+- name: Create presentation client directory
+ file:
+ path: /home/domjudge/presclient
+ state: directory
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+
+- name: Download and unpack presentation client
+ unarchive:
+ src: https://github.com/icpctools/icpctools/releases/download/v{{ pres_client_version }}/presentations-{{ pres_client_version }}.zip
+ dest: /home/domjudge/presclient
+ remote_src: true
+ owner: domjudge
+ group: domjudge
+
+- name: Find the presentation user
+ set_fact:
+ presentation_account: "{{ CDS_ACCOUNTS | selectattr('username', '==', 'presentation') }}"
+
+- name: Add presentation client script
+ template:
+ src: presclient.j2
+ dest: /usr/local/bin/presclient
+ owner: domjudge
+ group: domjudge
+ mode: 0755
+
+- name: Add presentation client autostart
+ copy:
+ src: presclient.desktop
+ dest: /home/domjudge/.config/autostart/presclient.desktop
+ owner: domjudge
+ group: domjudge
+ mode: 0755
diff --git a/provision-contest/ansible/roles/presclient/templates/presclient.j2 b/provision-contest/ansible/roles/presclient/templates/presclient.j2
new file mode 100644
index 00000000..23bec2db
--- /dev/null
+++ b/provision-contest/ansible/roles/presclient/templates/presclient.j2
@@ -0,0 +1 @@
+/home/domjudge/presclient/client.sh https://{{ CDS_HOSTNAME }}/api/contests/{{ PRESCLIENT_CONTEST }} {{ presentation_account[0].username }} {{ presentation_account[0].password }} --name {{ ansible_hostname }}
diff --git a/provision-contest/ansible/roles/prometheus_target_all/defaults/main.yml b/provision-contest/ansible/roles/prometheus_target_all/defaults/main.yml
index 0686e6c9..b009bed1 100644
--- a/provision-contest/ansible/roles/prometheus_target_all/defaults/main.yml
+++ b/provision-contest/ansible/roles/prometheus_target_all/defaults/main.yml
@@ -1 +1,4 @@
prom: true
+GROUP_PREFIXES:
+ - 'online-'
+ - ''
diff --git a/provision-contest/ansible/roles/prometheus_target_all/files/.gitignore b/provision-contest/ansible/roles/prometheus_target_all/files/.gitignore
new file mode 100644
index 00000000..40a4a51a
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_all/files/.gitignore
@@ -0,0 +1,3 @@
+*.key
+*.crt
+promtail-linux-amd64.zip
diff --git a/provision-contest/ansible/roles/prometheus_target_all/files/71-push.conf b/provision-contest/ansible/roles/prometheus_target_all/files/71-push.conf
new file mode 100644
index 00000000..ec38fdb5
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_all/files/71-push.conf
@@ -0,0 +1 @@
+*.* @127.0.0.1:1514
diff --git a/provision-contest/ansible/roles/prometheus_target_all/files/promtail.service b/provision-contest/ansible/roles/prometheus_target_all/files/promtail.service
index 10606214..10a56c25 100644
--- a/provision-contest/ansible/roles/prometheus_target_all/files/promtail.service
+++ b/provision-contest/ansible/roles/prometheus_target_all/files/promtail.service
@@ -4,7 +4,7 @@ After=network.target
[Service]
Type=simple
-ExecStart=/usr/bin/promtail-linux-amd64 --config.file /etc/promtail/promtail-local-config.yaml
+ExecStart=/usr/bin/promtail-linux-amd64 --config.file /etc/promtail/promtail-local-config.yml
[Install]
WantedBy=multi-user.target
diff --git a/provision-contest/ansible/roles/prometheus_target_all/handlers/main.yml b/provision-contest/ansible/roles/prometheus_target_all/handlers/main.yml
index 4d60f810..fa9d6a7d 100644
--- a/provision-contest/ansible/roles/prometheus_target_all/handlers/main.yml
+++ b/provision-contest/ansible/roles/prometheus_target_all/handlers/main.yml
@@ -1,7 +1,19 @@
---
-- name: restart promtail
+- name: Restart rsyslog
+ service:
+ name: rsyslog
+ enabled: true
+ state: restarted
+
+- name: Restart promtail
service:
name: promtail
enabled: true
state: restarted
daemon_reload: true
+
+- name: Restart node-exporter
+ service:
+ name: prometheus-node-exporter
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/prometheus_target_all/tasks/main.yml b/provision-contest/ansible/roles/prometheus_target_all/tasks/main.yml
index 6716f037..07661ce4 100644
--- a/provision-contest/ansible/roles/prometheus_target_all/tasks/main.yml
+++ b/provision-contest/ansible/roles/prometheus_target_all/tasks/main.yml
@@ -1,54 +1,120 @@
---
# This tasks gathers default system metrics.
-- name: install required packages
+- name: Install required packages
apt:
state: present
pkg:
- prometheus-node-exporter
-# Setup promtail which sends our logs
-- name: Install promtail
- unarchive:
- src: https://github.com/grafana/loki/releases/download/v2.5.0/promtail-linux-amd64.zip
- dest: /usr/bin/
- remote_src: true
- owner: domjudge
- group: domjudge
- when: prom
-
-- name: Dir for promtail settings
+- name: Collect prometheus settings
file:
- state: directory
- path: /etc/promtail
+ path: /etc/prometheus
owner: root
group: root
mode: 0755
- when: prom
+ state: directory
-- name: Set promtail settings
+- name: Install SSL server certificates
copy:
- src: promtail-local-config.yaml
- dest: /etc/promtail/
+ src: "node_exporter.{{ item }}"
+ dest: "/etc/prometheus/node_exporter.{{ item }}"
owner: root
group: root
mode: 0644
- when: prom
- notify: restart promtail
+ loop:
+ - crt
+ - key
-- name: Setup promtail systemd
+- name: Get HTPassword
+ delegate_to: localhost
+ become: false
+ shell: "echo {{ PROMETHEUS_PASS }} | htpasswd -inBC 10 \"\" | tr -d ':\n'"
+ register: htpassd_shell
+
+- name: Store HTPassword for nginx wrapper
copy:
- src: promtail.service
- dest: /etc/systemd/system/
- mode: 0655
+ content: "prometheus:{{ htpassd_shell.stdout }}"
+ dest: /etc/prometheus/.htpasswd
owner: root
group: root
- when: prom
- notify: restart promtail
+ mode: 0644
+
+- name: Set certificate to encrypt node_exporter traffic
+ template:
+ owner: prometheus
+ group: prometheus
+ mode: 0644
+ src: web.yml.j2
+ dest: /etc/prometheus/prometheus-authentication.yml
+
+- name: Scrape with TLS encryption
+ lineinfile:
+ dest: /etc/default/prometheus-node-exporter
+ state: present
+ regexp: '^ARGS=""'
+ line: 'ARGS="--web.config /etc/prometheus/prometheus-authentication.yml"'
+ notify: Restart node-exporter
-- name: Start promtail service
- service:
- name: promtail
- state: started
- enabled: true
+# Setup promtail which sends our logs
+- name: Setup promtail to ship logs to loki (and grafana)
when: prom
+ block:
+ - name: Install promtail
+ unarchive:
+ src: promtail-linux-amd64.zip
+ dest: /usr/bin/
+ remote_src: false
+ owner: domjudge
+ group: domjudge
+ when: ICPC_IMAGE
+
+ - name: Install promtail
+ unarchive:
+ src: https://github.com/grafana/loki/releases/download/v3.0.1/promtail-linux-amd64.zip
+ dest: /usr/bin/
+ remote_src: true
+ owner: domjudge
+ group: domjudge
+ when: not ICPC_IMAGE
+
+ - name: Dir for promtail settings
+ file:
+ state: directory
+ path: /etc/promtail
+ owner: root
+ group: root
+ mode: 0755
+
+ - name: Set promtail settings
+ template:
+ src: promtail-local-config.yml.j2
+ dest: /etc/promtail/promtail-local-config.yml
+ owner: root
+ group: root
+ mode: 0644
+ notify: Restart promtail
+
+ - name: Setup promtail push
+ copy:
+ src: 71-push.conf
+ dest: /etc/rsyslog.d/
+ mode: 0655
+ owner: root
+ group: root
+ notify: Restart rsyslog
+
+ - name: Setup promtail systemd
+ copy:
+ src: promtail.service
+ dest: /etc/systemd/system/
+ mode: 0655
+ owner: root
+ group: root
+ notify: Restart promtail
+
+ - name: Start promtail service
+ service:
+ name: promtail
+ state: started
+ enabled: true
diff --git a/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yaml.j2 b/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yaml.j2
deleted file mode 100644
index af053e76..00000000
--- a/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yaml.j2
+++ /dev/null
@@ -1,20 +0,0 @@
-server:
- http_listen_port: 9080
- #grpc_listen_port: 0
-
-positions:
- filename: /var/log/positions.yaml
-
-clients:
-{% for host in groups["grafana"] %}
- - url: https://{{ hostvars[host].ansible_host }}
-{% endfor %}
-
-scrape_configs:
-- job_name: system
- static_configs:
- - targets:
- - localhost
- labels:
- job: varlogs
- __path__: /var/log/*log
diff --git a/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yml.j2 b/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yml.j2
new file mode 100644
index 00000000..6e6c6d35
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_all/templates/promtail-local-config.yml.j2
@@ -0,0 +1,37 @@
+server:
+ http_listen_port: 9080
+ grpc_listen_port: 19080
+
+positions:
+ filename: /var/tmp/promtail-syslog-positions.yml
+
+clients:
+{% for host in groups["grafana"] %}
+ - url: http://{{ hostvars[host].ansible_host }}:3100/loki/api/v1/push
+{% endfor %}
+
+scrape_configs:
+ - job_name: system
+ static_configs:
+ - labels:
+ __path__: /var/log/**/*log
+{% for group_prefix in GROUP_PREFIXES %}
+{% if ansible_fqdn in groups[group_prefix+'judgehost'] %}
+ - job_name: judgehostlogs
+ static_configs:
+ - labels:
+ __path__: '/opt/domjudge/output/log/*'
+{% endif %}
+{% if ansible_fqdn in groups[group_prefix+'domserver'] %}
+ - job_name: webapplogs
+ static_configs:
+ - labels:
+ __path__: '/opt/domjudge/webapp/var/log/*'
+{% endif %}
+{% endfor %}
+ # See: https://alexandre.deverteuil.net/post/syslog-relay-for-loki/
+ - job_name: syslog
+ syslog:
+ listen_address: 0.0.0.0:1514
+ labels:
+ job: syslog
diff --git a/provision-contest/ansible/roles/prometheus_target_all/templates/web.yml.j2 b/provision-contest/ansible/roles/prometheus_target_all/templates/web.yml.j2
new file mode 100644
index 00000000..4d1a35ac
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_all/templates/web.yml.j2
@@ -0,0 +1,5 @@
+basic_auth_users:
+ prometheus: {{ htpassd_shell.stdout }}
+tls_server_config:
+ cert_file: /etc/prometheus/node_exporter.crt
+ key_file: /etc/prometheus/node_exporter.key
diff --git a/provision-contest/ansible/roles/prometheus_target_web/files/.gitignore b/provision-contest/ansible/roles/prometheus_target_web/files/.gitignore
index 403c59b9..599e1479 100644
--- a/provision-contest/ansible/roles/prometheus_target_web/files/.gitignore
+++ b/provision-contest/ansible/roles/prometheus_target_web/files/.gitignore
@@ -1 +1,2 @@
/php-fpm_exporter*.tar.gz
+/php-fpm.tar.gz
diff --git a/provision-contest/ansible/roles/prometheus_target_web/files/nginx-status.conf b/provision-contest/ansible/roles/prometheus_target_web/files/nginx-status.conf
deleted file mode 100644
index 5617c246..00000000
--- a/provision-contest/ansible/roles/prometheus_target_web/files/nginx-status.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-server {
- listen 8787;
- listen [::]:8787;
- server_name _default_;
-
- location = /basic_status {
- stub_status;
- }
-}
diff --git a/provision-contest/ansible/roles/prometheus_target_web/files/php-fpm-exporter.service b/provision-contest/ansible/roles/prometheus_target_web/files/php-fpm-exporter.service
index 94e5185e..9d6ca255 100644
--- a/provision-contest/ansible/roles/prometheus_target_web/files/php-fpm-exporter.service
+++ b/provision-contest/ansible/roles/prometheus_target_web/files/php-fpm-exporter.service
@@ -4,7 +4,7 @@ Documentation=https://github.com/hipages/php-fpm_exporter
[Service]
User=www-data
Restart=always
-ExecStart=/usr/bin/php-fpm_exporter server --phpfpm.fix-process-count --phpfpm.scrape-uri unix:///var/run/php-fpm-domjudge.sock;/fpm_status
+ExecStart=/usr/bin/php-fpm_exporter server --web.listen-address :19253 --phpfpm.fix-process-count --phpfpm.scrape-uri unix:///var/run/php-fpm-domjudge.sock;/fpm_status
ExecReload=/bin/kill -HUP $MAINPID
TimeoutStopSec=20s
SendSIGKILL=no
diff --git a/provision-contest/ansible/roles/prometheus_target_web/handlers/main.yml b/provision-contest/ansible/roles/prometheus_target_web/handlers/main.yml
index b5739a5f..b3fbd2e0 100644
--- a/provision-contest/ansible/roles/prometheus_target_web/handlers/main.yml
+++ b/provision-contest/ansible/roles/prometheus_target_web/handlers/main.yml
@@ -1,12 +1,19 @@
---
-- name: restart php-exporter
- service: name=php-fpm-exporter enabled=true state=restarted
+- name: Restart php-exporter
+ service:
+ name: php-fpm-exporter
+ enabled: true
+ state: restarted
+ daemon_reload: true
-- name: restart nginx
- service: name=nginx enabled=true state=restarted
+- name: Restart nginx-exporter
+ service:
+ name: prometheus-nginx-exporter
+ enabled: true
+ state: restarted
-- name: restart nginx-exporter
- service: name=prometheus-nginx-exporter enabled=true state=restarted
-
-- name: restart mysqld-exporter
- service: name=prometheus-mysqld-exporter enabled=true state=restarted
+- name: Restart mysqld-exporter
+ service:
+ name: prometheus-mysqld-exporter
+ enabled: true
+ state: restarted
diff --git a/provision-contest/ansible/roles/prometheus_target_web/meta/main.yml b/provision-contest/ansible/roles/prometheus_target_web/meta/main.yml
new file mode 100644
index 00000000..24a09a19
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_web/meta/main.yml
@@ -0,0 +1,5 @@
+# Role dependencies
+---
+dependencies:
+ - role: prometheus_target_all
+ tags: prometheus_target_all
diff --git a/provision-contest/ansible/roles/prometheus_target_web/tasks/main.yml b/provision-contest/ansible/roles/prometheus_target_web/tasks/main.yml
index 77660578..609c7db8 100644
--- a/provision-contest/ansible/roles/prometheus_target_web/tasks/main.yml
+++ b/provision-contest/ansible/roles/prometheus_target_web/tasks/main.yml
@@ -5,8 +5,8 @@
apt:
state: present
pkg:
- - prometheus-mysqld-exporter
- notify: restart nginx-exporter
+ - prometheus-nginx-exporter
+ notify: Restart nginx-exporter
- name: Expose MariaDB metrics
when: MARIADB
@@ -14,13 +14,21 @@
state: present
pkg:
- prometheus-mysqld-exporter
- notify: restart mysqld-exporter
+ notify: Restart mysqld-exporter
+
+- name: Scrape mysql exporter with TLS encryption
+ lineinfile:
+ dest: /etc/default/prometheus-mysqld-exporter
+ state: present
+ regexp: '^ARGS=""'
+ line: 'ARGS="--web.config.file /etc/prometheus/prometheus-authentication.yml"'
+ notify: Restart mysqld-exporter
# Gather PHP-FPM statistics
# The exporter from this is currently not in deb sources
# so we need to download this from GitHub see the README in files
- name: Install PHP-fpm exporter binary
- when: FPM
+ when: FPM and not WF_RESTRICTED_NETWORK
ansible.builtin.unarchive:
src: https://github.com/hipages/php-fpm_exporter/releases/download/v2.0.4/php-fpm_exporter_2.0.4_linux_amd64.tar.gz
dest: /usr/bin/
@@ -28,30 +36,52 @@
exclude:
- LICENSE
- README.md
- notify: restart php-exporter
+ notify: Restart php-exporter
+
+- name: Install PHP-fpm exporter binary (local cache)
+ when: FPM and WF_RESTRICTED_NETWORK
+ block:
+ - name: Copy local php-fpm deb to monitoring host
+ copy:
+ src: php-fpm.tar.gz
+ dest: /srv/php-fpm.tar.gz
+
+ - name: Install local archive on remote
+ ansible.builtin.unarchive:
+ src: /srv/php-fpm.tar.gz
+ dest: /usr/bin/
+ remote_src: true
+ exclude:
+ - LICENSE
+ - README.md
+ notify: Restart php-exporter
- name: Export PHP-FPM metrics
when: FPM
synchronize:
src: php-fpm-exporter.service
dest: /etc/systemd/system/php-fpm-exporter.service
- notify: restart php-exporter
+ notify: Restart php-exporter
# Gather NGINX statistics,
# Observe that we use the observed process itself in the monitoring
- name: Get NGINX status
- synchronize:
- src: nginx-status.conf
+ template:
+ src: nginx-status.conf.j2
dest: /etc/nginx/sites-enabled/nginx-status.conf
- notify: restart nginx
+ mode: 0644
+ notify: Restart nginx
+# In the future add: --web.config /etc/prometheus/prometheus-authentication.yml"'
+# see: https://github.com/nginxinc/nginx-prometheus-exporter
+# The version at the WFLuxor in the repository is not new enough
- name: Prometheus nginx exporter
lineinfile:
dest: /etc/default/prometheus-nginx-exporter
state: present
regexp: '^ARGS=""'
- line: 'ARGS="-nginx.scrape-uri=http://localhost:8787/basic_status"'
- notify: restart nginx-exporter
+ line: 'ARGS="-web.listen-address=127.0.0.1:19113 -nginx.scrape-uri=http://localhost:8787/basic_status"'
+ notify: Restart nginx-exporter
- name: Create storage dir for exporter settings
when: MARIADB
@@ -71,4 +101,4 @@
dest: /var/lib/prometheus/.my.cnf
owner: prometheus
mode: 0644
- notify: restart mysqld-exporter
+ notify: Restart mysqld-exporter
diff --git a/provision-contest/ansible/roles/prometheus_target_web/templates/nginx-status.conf.j2 b/provision-contest/ansible/roles/prometheus_target_web/templates/nginx-status.conf.j2
new file mode 100644
index 00000000..e5397fdb
--- /dev/null
+++ b/provision-contest/ansible/roles/prometheus_target_web/templates/nginx-status.conf.j2
@@ -0,0 +1,34 @@
+server {
+ listen 127.0.0.1:8787;
+ server_name _default_;
+
+ location = /basic_status {
+ stub_status;
+ }
+}
+
+server {
+ listen 0.0.0.0:9113 ssl;
+ ssl_certificate /etc/prometheus/node_exporter.crt;
+ ssl_certificate_key /etc/prometheus/node_exporter.key;
+ ssl_protocols TLSv1.3;
+
+ auth_basic "Prometheus scraping";
+ auth_basic_user_file /etc/prometheus/.htpasswd;
+ location / {
+ proxy_pass http://127.0.0.1:19113;
+ }
+}
+
+server {
+ listen 0.0.0.0:9253 ssl;
+ ssl_certificate /etc/prometheus/node_exporter.crt;
+ ssl_certificate_key /etc/prometheus/node_exporter.key;
+ ssl_protocols TLSv1.3;
+
+ auth_basic "Prometheus scraping";
+ auth_basic_user_file /etc/prometheus/.htpasswd;
+ location / {
+ proxy_pass http://127.0.0.1:19253;
+ }
+}
diff --git a/provision-contest/ansible/roles/scoreboard/handlers/main.yml b/provision-contest/ansible/roles/scoreboard/handlers/main.yml
deleted file mode 100644
index 1d442fdc..00000000
--- a/provision-contest/ansible/roles/scoreboard/handlers/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-# Define here handlers associated to this role.
-
-- name: restart nginx
- service: name=nginx enabled=true state=restarted
diff --git a/provision-contest/ansible/roles/scoreboard/tasks/main.yml b/provision-contest/ansible/roles/scoreboard/tasks/main.yml
index f5726d64..94c2dd60 100644
--- a/provision-contest/ansible/roles/scoreboard/tasks/main.yml
+++ b/provision-contest/ansible/roles/scoreboard/tasks/main.yml
@@ -1,7 +1,7 @@
---
# These tasks configure a static scoreboard
-- name: create directories for styling, scripts, fonts and flags
+- name: Create directories for styling, scripts, fonts and flags
file:
path: /home/domjudge/scoreboard/{{ item }}
state: directory
@@ -14,7 +14,15 @@
- webfonts
- flags
-- name: download required assets
+- name: Grant www-data permisions to domjudge homedir
+ acl:
+ path: /home/domjudge
+ entity: www-data
+ etype: user
+ permissions: rwx
+ state: present
+
+- name: Download required assets
get_url:
url: "{{ DOMSERVER_URL }}/{{ item }}"
dest: /home/domjudge/scoreboard/{{ item }}
@@ -26,6 +34,7 @@
- css/bootstrap.min.css
- css/fontawesome-all.min.css
- js/jquery.min.js
+ - js/jquery.debounce.min.js
- js/bootstrap.bundle.min.js
- js/domjudge.js
- style_domjudge.css
@@ -38,7 +47,7 @@
- webfonts/fa-v4compatibility.ttf
- webfonts/fa-v4compatibility.woff2
-- name: install contest images
+- name: Install contest images
synchronize:
src: files/domjudge-public/
dest: "/home/domjudge/scoreboard/"
@@ -47,7 +56,7 @@
become: true
become_user: domjudge
-- name: download and unpack flag icons package
+- name: Download and unpack flag icons package
unarchive:
src: https://github.com/lipis/flag-icons/archive/refs/tags/3.5.0.zip
dest: /tmp
@@ -55,7 +64,7 @@
owner: domjudge
group: domjudge
-- name: copy flag icon SVG's
+- name: Copy flag icon SVG's
copy:
src: /tmp/flag-icons-3.5.0/flags/
dest: /home/domjudge/scoreboard/flags/
@@ -64,7 +73,7 @@
group: domjudge
mode: 0755
-- name: create bin directory
+- name: Create bin directory
file:
path: /home/domjudge/bin
state: directory
@@ -72,7 +81,7 @@
group: domjudge
mode: 0755
-- name: install update scoreboard script
+- name: Install update scoreboard script
template:
src: update-scoreboard.sh.j2
dest: "/home/domjudge/bin/update-scoreboard.sh"
@@ -81,34 +90,34 @@
mode: 0755
# Ansible cron task defaults to * for all cron parts
-- name: set up cron to update scoreboard
+- name: Set up cron to update scoreboard
cron:
name: update public scoreboard
job: /home/domjudge/bin/update-scoreboard.sh
user: domjudge
-- name: install nginx
+- name: Install nginx
apt:
state: present
pkg:
- nginx
-- name: add scoreboard nginx conf
+- name: Add scoreboard nginx conf
template:
src: scoreboard.conf.j2
dest: /etc/nginx/sites-available/scoreboard.conf
mode: 0644
- notify: restart nginx
+ notify: Restart nginx
-- name: enable nginx conf for static scoreboard
+- name: Enable nginx conf for static scoreboard
file:
src: /etc/nginx/sites-available/scoreboard.conf
dest: /etc/nginx/sites-enabled/scoreboard.conf
state: link
- notify: restart nginx
+ notify: Restart nginx
-- name: disable default nginx site
+- name: Disable default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
- notify: restart nginx
+ notify: Restart nginx
diff --git a/provision-contest/ansible/roles/ssh/defaults/main.yml b/provision-contest/ansible/roles/ssh/defaults/main.yml
new file mode 100644
index 00000000..c54730df
--- /dev/null
+++ b/provision-contest/ansible/roles/ssh/defaults/main.yml
@@ -0,0 +1,11 @@
+github_ssh_user:
+ - meisterT
+ - nickygerritsen
+ - thijskh
+ - tuupke
+ - ubergeek42
+ - vmcj
+
+ssh_public_keys:
+ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICd/P3Mu0X0hE9yc+k4cU/eT67S4F4E8Lmxk4yYDM8qL jaap@diamond"
+ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrtJzsraIV4SFFkeu5HtxwRZKBOwn2dL/g1CJhaCwJs jaap@tiger"
diff --git a/provision-contest/ansible/roles/ssh/files/.gitignore b/provision-contest/ansible/roles/ssh/files/.gitignore
index ac18eab4..14aa70a0 100644
--- a/provision-contest/ansible/roles/ssh/files/.gitignore
+++ b/provision-contest/ansible/roles/ssh/files/.gitignore
@@ -1 +1 @@
-/id_rsa*
+/id_ed25519*
diff --git a/provision-contest/ansible/roles/ssh/files/config b/provision-contest/ansible/roles/ssh/files/config
index 9ba9ad25..774a3847 100644
--- a/provision-contest/ansible/roles/ssh/files/config
+++ b/provision-contest/ansible/roles/ssh/files/config
@@ -1,7 +1,10 @@
+# This file is managed by ansible, so any changes you make here will
+# get overwritten in the next ansible run. If you want to make local
+# changes, do this in the following include snippet.
+Include ~/.ssh/config-local
+
# Defaults used if no other host definitions apply.
# Add more host specific settings above here.
Host *
- NoHostAuthenticationForLocalhost yes
- ForwardAgent yes
- ForwardX11 yes
- ForwardX11Trusted yes
+ NoHostAuthenticationForLocalhost yes
+ ForwardAgent yes
diff --git a/provision-contest/ansible/roles/ssh/meta/main.yml b/provision-contest/ansible/roles/ssh/meta/main.yml
index 4a4f743a..a480e52b 100644
--- a/provision-contest/ansible/roles/ssh/meta/main.yml
+++ b/provision-contest/ansible/roles/ssh/meta/main.yml
@@ -2,3 +2,4 @@
---
dependencies:
- role: domjudge_user
+ tags: domjudge_user
diff --git a/provision-contest/ansible/roles/ssh/tasks/add-auth-keys.yml b/provision-contest/ansible/roles/ssh/tasks/add-auth-keys.yml
new file mode 100644
index 00000000..ac30db53
--- /dev/null
+++ b/provision-contest/ansible/roles/ssh/tasks/add-auth-keys.yml
@@ -0,0 +1,13 @@
+- name: "Ensure authorized_keys for {{ user_item }} contains keys from GitHub user accounts."
+ authorized_key:
+ user: "{{ user_item }}"
+ key: "https://github.com/{{ item }}.keys"
+ comment: "{{ item }}"
+ loop: "{{ github_ssh_user }}"
+ when: not WF_RESTRICTED_NETWORK or WF_GREEN
+
+- name: "Ensure authorized_keys for {{ user_item }} contains listed public keys."
+ authorized_key:
+ user: "{{ user_item }}"
+ key: "{{ item }}"
+ loop: "{{ ssh_public_keys }}"
diff --git a/provision-contest/ansible/roles/ssh/tasks/main.yml b/provision-contest/ansible/roles/ssh/tasks/main.yml
index 45e6a52c..d76681fc 100644
--- a/provision-contest/ansible/roles/ssh/tasks/main.yml
+++ b/provision-contest/ansible/roles/ssh/tasks/main.yml
@@ -18,8 +18,8 @@
mode: 0600
loop:
- config
- - id_rsa
- - id_rsa.pub
+ - id_ed25519
+ - id_ed25519.pub
- name: Copy shared SSH private/public key and config to root
copy:
@@ -28,14 +28,21 @@
mode: 0600
loop:
- config
- - id_rsa
- - id_rsa.pub
+ - id_ed25519
+ - id_ed25519.pub
- name: Add SSH key to authorized keys of domjudge and root
authorized_key:
user: "{{ item }}"
state: present
- key: "{{ lookup('file', 'id_rsa.pub') }}"
+ key: "{{ lookup('file', 'id_ed25519.pub') }}"
loop:
- domjudge
- root
+
+- include_tasks: add-auth-keys.yml
+ loop:
+ - domjudge
+ - root
+ loop_control:
+ loop_var: user_item
diff --git a/provision-contest/ansible/roles/ssh/vars/.gitignore b/provision-contest/ansible/roles/ssh/vars/.gitignore
new file mode 100644
index 00000000..1cda54be
--- /dev/null
+++ b/provision-contest/ansible/roles/ssh/vars/.gitignore
@@ -0,0 +1 @@
+*.yml
diff --git a/provision-contest/ansible/roles/ssl/handlers/main.yml b/provision-contest/ansible/roles/ssl/handlers/main.yml
index cf4078cf..3a5c2355 100644
--- a/provision-contest/ansible/roles/ssl/handlers/main.yml
+++ b/provision-contest/ansible/roles/ssl/handlers/main.yml
@@ -1,5 +1,3 @@
---
-# Define here handlers associated to this role.
-
-- name: update-ca-certificates
+- name: Update-ca-certificates
command: update-ca-certificates
diff --git a/provision-contest/ansible/roles/ssl/tasks/main.yml b/provision-contest/ansible/roles/ssl/tasks/main.yml
index 691c5210..13d28702 100644
--- a/provision-contest/ansible/roles/ssl/tasks/main.yml
+++ b/provision-contest/ansible/roles/ssl/tasks/main.yml
@@ -1,7 +1,7 @@
---
# These tasks install SSL certificates/keys
-- name: install SSL server certificates
+- name: Install SSL server certificates
copy:
src: "{{ item }}"
dest: /etc/ssl/certs/
@@ -10,9 +10,9 @@
mode: 0644
with_fileglob:
- "*.crt"
- notify: update-ca-certificates
+ notify: Update-ca-certificates
-- name: create CA certificates shared directory
+- name: Create CA certificates shared directory
file:
dest: /usr/local/share/ca-certificates
state: directory
@@ -20,7 +20,7 @@
group: root
mode: 0755
-- name: install SSL server certificates into CA certificates
+- name: Install SSL server certificates into CA certificates
copy:
src: "{{ item }}"
dest: /usr/local/share/ca-certificates
@@ -29,9 +29,9 @@
mode: 0644
with_fileglob:
- "*.crt"
- notify: update-ca-certificates
+ notify: Update-ca-certificates
-- name: install SSL private key files
+- name: Install SSL private key files
copy:
src: "{{ item }}"
dest: /etc/ssl/private/
@@ -40,5 +40,5 @@
mode: 0600
with_fileglob:
- "*.key"
- notify: update-ca-certificates
+ notify: Update-ca-certificates
when: INSTALL_SSL_PRIVATE_KEYS is defined and INSTALL_SSL_PRIVATE_KEYS
diff --git a/provision-contest/ansible/roles/system_fixes/handlers/main.yml b/provision-contest/ansible/roles/system_fixes/handlers/main.yml
index 572a8e29..a1b9179d 100644
--- a/provision-contest/ansible/roles/system_fixes/handlers/main.yml
+++ b/provision-contest/ansible/roles/system_fixes/handlers/main.yml
@@ -1,5 +1,6 @@
---
-# Define here handlers associated to this role.
-
-- name: update dconf
+- name: Update dconf
command: dconf update
+
+- name: Reboot the machine
+ reboot:
diff --git a/provision-contest/ansible/roles/system_fixes/tasks/main.yml b/provision-contest/ansible/roles/system_fixes/tasks/main.yml
index 115a45cf..358bf659 100644
--- a/provision-contest/ansible/roles/system_fixes/tasks/main.yml
+++ b/provision-contest/ansible/roles/system_fixes/tasks/main.yml
@@ -1,18 +1,26 @@
---
# These tasks perform miscellaneous fixes to the base system.
-- name: set timezone
+- name: Check installed PHP version
+ command: php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;'
+ register: php_version
+ changed_when: false
+ check_mode: no
+ ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Set timezone
timezone:
name: "{{ TIMEZONE }}"
-- name: set PHP timezone for CLI
+- name: Set PHP timezone for CLI
lineinfile:
- dest: /etc/php/7.4/cli/php.ini
+ dest: "/etc/php/{{ php_version.stdout }}/cli/php.ini"
state: present
regexp: 'date\.timezone\s*='
line: 'date.timezone = {{ TIMEZONE }}'
+ ignore_errors: "{{ ansible_check_mode }}"
-- name: enable bash completion globally
+- name: Enable bash completion globally
blockinfile:
path: /etc/bash.bashrc
insertafter: "# enable bash completion in interactive shells"
@@ -32,4 +40,36 @@
owner: root
group: root
mode: 0755
- notify: update dconf
+ notify: Update dconf
+ when: GRAPHICAL
+
+- name: Configure network for static IP address
+ when: STATIC_IP_ENABLED
+ block:
+ - name: Configure static IP address
+ template:
+ src: static_ip.j2
+ dest: /etc/network/interfaces.d/{{ STATIC_IP_INTERFACE }}
+ notify: Reboot the machine
+
+ - name: Load configuration from /etc/network/interfaces.d
+ lineinfile:
+ path: /etc/network/interfaces
+ line: 'source-directory /etc/network/interfaces.d'
+ notify: Reboot the machine
+
+ - name: Use 8.8.8.8 as DNS server
+ lineinfile:
+ path: /etc/systemd/resolved.conf
+ regexp: '^#?DNS='
+ line: 'DNS=8.8.8.8'
+ notify: Reboot the machine
+
+- name: Enable etckeeper autocommits every hour
+ file:
+ src: /etc/cron.daily/etckeeper
+ dest: /etc/cron.hourly/etckeeper
+ state: link
+
+- name: Flush handlers
+ meta: flush_handlers
diff --git a/provision-contest/ansible/roles/system_fixes/templates/static_ip.j2 b/provision-contest/ansible/roles/system_fixes/templates/static_ip.j2
new file mode 100644
index 00000000..78993a29
--- /dev/null
+++ b/provision-contest/ansible/roles/system_fixes/templates/static_ip.j2
@@ -0,0 +1,5 @@
+allow-hotplug {{ STATIC_IP_INTERFACE }}
+iface {{ STATIC_IP_INTERFACE }} inet static
+ address {{ ansible_host }}
+ netmask {{ STATIC_IP_NETMASK }}
+ gateway {{ STATIC_IP_GATEWAY }}
diff --git a/provision-contest/ansible/scoreboard.yml b/provision-contest/ansible/scoreboard.yml
index 221ceaf7..3b0ce472 100644
--- a/provision-contest/ansible/scoreboard.yml
+++ b/provision-contest/ansible/scoreboard.yml
@@ -1,11 +1,13 @@
---
# This playbook installs the static scoreboard
-- name: setup static scoreboard
+- name: Setup static scoreboard
hosts: scoreboard
vars:
host_type: scoreboard
become: true
+ handlers:
+ - import_tasks: handlers.yml
roles:
- role: base_packages
tags: base_packages
diff --git a/provision-contest/create_samples_zip.py b/provision-contest/create_samples_zip.py
new file mode 100755
index 00000000..45b69529
--- /dev/null
+++ b/provision-contest/create_samples_zip.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python3
+import sys
+import os
+import yaml
+import zipfile
+import argparse
+
+DEFAULT_OUTPUT = 'samps.zip'
+
+def add_to_zip(zipf, source, dest, predicate=None):
+ for root, dirs, files in os.walk(source):
+ for fname in files:
+ path = os.path.join(root, fname)
+ if predicate is not None and not predicate(path):
+ continue
+ zipf.write(path, os.path.join(dest, os.path.relpath(path, source)))
+
+def public_sample_file(path):
+ testcase, ext = os.path.splitext(path)
+
+ if ext not in {'.in', '.ans', '.interaction'}:
+ return False
+
+ if ext != '.interaction' and os.path.exists(testcase + '.interaction'):
+ return False
+
+ return True
+
+def main():
+ parser = argparse.ArgumentParser(prog = 'create_samples_zip', description = 'generate the samples.zip file from a contest archive')
+ parser.add_argument('-o', '--output', default = DEFAULT_OUTPUT, help = 'the output path for the samples zip')
+ parser.add_argument('config_path', help = 'the path to the contest archive')
+ args = parser.parse_args()
+
+ config_path = args.config_path
+ output_path = args.output
+ if os.path.isdir(output_path):
+ output_path = os.path.join(output_path, DEFAULT_OUTPUT)
+
+ with open(os.path.join(config_path, 'problemset.yaml'), 'r') as f:
+ problemset = yaml.load(f, Loader=yaml.Loader)
+
+ with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
+ for problem in problemset['problems']:
+
+ add_to_zip(zipf,
+ os.path.join(config_path, problem['short-name'], 'data', 'sample'),
+ problem['letter'],
+ public_sample_file)
+
+ add_to_zip(zipf,
+ os.path.join(config_path, problem['short-name'], 'attachments'),
+ os.path.join(problem['letter'], 'attachments'))
+
+if __name__ == '__main__':
+ main()
+
diff --git a/provision-contest/disable-turboboost_ht b/provision-contest/disable-turboboost_ht
index 272d0ee2..07ec3812 100755
--- a/provision-contest/disable-turboboost_ht
+++ b/provision-contest/disable-turboboost_ht
@@ -4,53 +4,109 @@ set -eu -o pipefail
shopt -s extglob
declare -A core_ids
+declare -a disabled_cores
-for cpu in /sys/devices/system/cpu/cpu* ; do
- [[ $(basename $cpu) =~ ^cpu[0-9]+$ ]] || continue
-
- # Reenable stuff in case we are rerunning this script.
- [ -f $cpu/online ] && echo 1 > $cpu/online
- if [ -f $cpu/cpufreq/scaling_governor ]; then
- chmod u+w $cpu/cpufreq/scaling_governor
- fi
-
- # Set governor to performance and do not allow changes later on.
- if [ -f $cpu/cpufreq/scaling_governor ]; then
- echo performance > $cpu/cpufreq/scaling_governor
- chmod a-w $cpu/cpufreq/scaling_governor
- fi
-
- # Disable all but one thread on each core.
- core_id=$(cat $cpu/topology/core_id)
- if [[ ${core_ids[$core_id]:-} ]]; then
- echo 0 > $cpu/online
- else
- core_ids[$core_id]=1
- fi
+disable_cpu () {
+ cpu="$1"
+ echo 0 > $cpu/online
+ disabled_cores+=("${cpu##*/}")
+}
+
+store_isolcpus_fact="/var/tmp/isolcpus"
+
+# Learn all efficency cores if they exist
+cpu_list=()
+if [ -f /sys/devices/cpu_atom/cpus ]; then
+ range="$(cat /sys/devices/cpu_atom/cpus)"
+ IFS='-' read -r start end <<< "$range"
+ # shellcheck disable=SC2207,SC2175
+ cpu_list=($(eval echo {$start..$end}))
+fi
+
+# shellcheck disable=SC2012
+for cpu in $(ls -1d /sys/devices/system/cpu/cpu* | sort --version-sort) ; do
+ [[ $(basename $cpu) =~ ^cpu[0-9]+$ ]] || continue
+
+ # Reenable stuff in case we are rerunning this script.
+ [ -f $cpu/online ] && echo 1 > $cpu/online
+ if [ -f $cpu/cpufreq/scaling_governor ]; then
+ chmod u+w $cpu/cpufreq/scaling_governor
+ fi
+ for boundary in min max; do
+ if [ -f $cpu/cpufreq/scaling_${boundary}_freq ]; then
+ chmod u+w $cpu/cpufreq/scaling_${boundary}_freq
+ fi
+ done
+
+ # Set governor to performance and do not allow changes later on.
+ if [ -f $cpu/cpufreq/scaling_governor ]; then
+ echo performance > $cpu/cpufreq/scaling_governor
+ chmod a-w $cpu/cpufreq/scaling_governor
+ fi
+
+ # Hardware maximum performance.
+ if [ -f $cpu/cpufreq/scaling_min_freq ] && [ -f $cpu/cpufreq/scaling_max_freq ]; then
+ cp $cpu/cpufreq/scaling_{max,min}_freq
+ chmod a-w $cpu/cpufreq/scaling_{max,min}_freq
+ fi
+
+ # Disable all but one thread on each core per socket. Both core_id and physical_package_id
+ # are numbers so it must be ensured that for the following examples are seen as distinct:
+ # - core_id=1, physical_package=11
+ # - core_id=11, physical_package=1
+ # Simple concatenation would result in the string '111' for both cores. Though `cat`
+ # adds a newline after each file, we do not want to rely on `cat` to always add this
+ # 'delimiter'.
+ core_id=$(cat $cpu/topology/core_id | tr -d '\n')'-'$(cat $cpu/topology/physical_package_id | tr -d '\n')
+
+ # Disable all efficiency cores
+ found=0
+ for efficiency_core in "${cpu_list[@]}"; do
+ if [[ "$cpu" == "/sys/devices/system/cpu/cpu$efficiency_core" ]]; then
+ disable_cpu $cpu
+ found=1
+ break
+ fi
+ done
+ if [ "$found" -eq "1" ]; then
+ continue
+ fi
+
+ if [[ ${core_ids[$core_id]:-} ]]; then
+ disable_cpu $cpu
+ else
+ core_ids[$core_id]=1
+ fi
done
+if [ -n "${store_isolcpus_fact}" ]; then
+ csv_string=$(IFS=, ; echo "${disabled_cores[*]}")
+ echo "$csv_string" > "$store_isolcpus_fact"
+fi
+
DIR_INTEL=/sys/devices/system/cpu/intel_pstate
DIR_AMD=/sys/devices/system/cpu/cpufreq
+# Same for some ARM CPUs
+FILE_AMD=$DIR_AMD/boost
if [ -d $DIR_INTEL ]; then
- # now disable turbo boost
- FILE=$DIR_INTEL/no_turbo
- echo -n 1 > $FILE || echo "Could not write to '$FILE', ignoring for now..."
- if [ $(cat $FILE) -ne 1 ]; then
- echo "Error: turboboost still enabled!"
- exit 1
- fi
-
- # increase freq from powersaving to normal, but don't overclock
- echo 100 > $DIR_INTEL/min_perf_pct
- echo 100 > $DIR_INTEL/max_perf_pct
-elif [ -d $DIR_AMD ]; then
- # now disable boosting
- FILE=$DIR_AMD/boost
- echo -n 0 > $FILE || echo "Could not write to '$FILE', ignoring for now..."
- if [ $(cat $FILE) -ne 0 ]; then
- echo "Error: turboboost still enabled!"
- exit 1
- fi
+ # now disable turbo boost
+ FILE=$DIR_INTEL/no_turbo
+ echo -n 1 > $FILE || echo "Could not write to '$FILE', ignoring for now..."
+ if [ $(cat $FILE) -ne 1 ]; then
+ echo "Error: turboboost still enabled!"
+ exit 1
+ fi
+
+ # increase freq from powersaving to normal, but don't overclock
+ echo 100 > $DIR_INTEL/min_perf_pct
+ echo 100 > $DIR_INTEL/max_perf_pct
+elif [ -f $FILE_AMD ]; then
+ # now disable boosting
+ echo -n 0 > $FILE_AMD || echo "Could not write to '$FILE_AMD', ignoring for now..."
+ if [ $(cat $FILE_AMD) -ne 0 ]; then
+ echo "Error: turboboost still enabled!"
+ exit 1
+ fi
else
- echo "Warning: kernel (turbo) boost config not found in '$DIR_INTEL' or '$DIR_AMD'."
+ echo "Warning: kernel (turbo) boost config not found in '$DIR_INTEL' or '$DIR_AMD'."
fi
diff --git a/provision-contest/dump-api.sh b/provision-contest/dump-api.sh
index 4eec3f3c..f04f21f1 100755
--- a/provision-contest/dump-api.sh
+++ b/provision-contest/dump-api.sh
@@ -6,10 +6,10 @@ baseurl="https://domjudge/api/contests"
mkdir -p "$where"
-for endpoint in judgement-types languages problems groups organizations teams state submissions judgements runs clarifications scoreboard; do
+for endpoint in judgement-types languages problems groups organizations teams state submissions judgements runs clarifications scoreboard awards; do
http GET "$baseurl/$contest/$endpoint" | python -mjson.tool > "$where/${endpoint}.json"
done
endpoint=contest
http GET "$baseurl/$contest" | python -mjson.tool > "$where/${endpoint}.json"
endpoint=event-feed
-http GET "$baseurl/$contest/${endpoint}?stream=0" > "$where/${endpoint}.json"
+http GET "$baseurl/$contest/${endpoint}?stream=0" > "$where/${endpoint}.ndjson"
diff --git a/provision-contest/replay.py b/provision-contest/replay.py
index e945e60a..31f84145 100755
--- a/provision-contest/replay.py
+++ b/provision-contest/replay.py
@@ -1,15 +1,16 @@
#!/usr/bin/python3
+import argparse
from halo import Halo
import json
-from pprint import pprint
from zipfile import ZipFile
from datetime import datetime
+from datetime import timedelta
+from itertools import dropwhile
import time
import sys
import random
import requests
-import re
import logging
logging.basicConfig(
@@ -18,15 +19,28 @@
format='%(asctime)s - \x1b[34;1m%(message)s\x1b[0m',
)
-if len(sys.argv) == 1 or len(sys.argv) > 4:
- print(f'Usage: {sys.argv[0]} [ []')
- sys.exit(-1)
-
-api_url = sys.argv[1]
-if len(sys.argv) >= 3:
- contest = sys.argv[2]
+parser = argparse.ArgumentParser(
+ formatter_class=argparse.RawTextHelpFormatter,
+ description='Replay a contest.')
+
+parser.add_argument('api_url', help='Contest API URL.')
+parser.add_argument('-c', '--contest', help='''submit for contest with ID CONTEST.
+ Mandatory when more than one contest is active.''')
+parser.add_argument('-s', '--submissionid', help='submission to start at.')
+parser.add_argument('--insecure', help='do not verify SSL certificate', action='store_true')
+parser.add_argument('-r', '--no_remap_teams', help='do not remap team ID\'s to team ID\'s of contest from API.', action='store_true')
+parser.add_argument('-I', '--ignore_teamids', help='Completely randomize teamids during replay, not storing any mapping.', action='store_true')
+parser.add_argument('-i', '--internal_data_source', help='The API uses an internal API source.', action='store_true')
+parser.add_argument('-f', '--simulation_speed', help='Speed up replay speed by this factor.')
+
+args = parser.parse_args()
+
+api_url = args.api_url
+verify = not args.insecure
+if args.contest:
+ contest = args.contest
else:
- contests = requests.get(f'{api_url}/contests').json()
+ contests = requests.get(f'{api_url}/contests', verify=verify).json()
if len(contests) == 1:
contest = contests[0]['id']
else:
@@ -40,17 +54,60 @@
submissions = json.load(open('submissions.json'))
logging.info(f'Loaded {len(submissions)} submissions.')
-contest_data = requests.get(f'{api_url}/contests/{contest}').json()
+problem_data = requests.get(f'{api_url}/contests/{contest}/problems', verify=verify).json()
+known_problem_ids = set([p['id'] for p in problem_data])
+used_problem_ids = set([s['problem_id'] for s in submissions])
+unknown_problem_ids = used_problem_ids.difference(known_problem_ids)
+if unknown_problem_ids:
+ if len(unknown_problem_ids) == len(used_problem_ids):
+ logging.critical('None of the used problem IDs is known.')
+ sys.exit(-1)
+ logging.error(f'Some problem IDs are used but not known: {unknown_problem_ids}')
+
+contest_data = requests.get(f'{api_url}/contests/{contest}', verify=verify).json()
+if 'code' in contest_data and 'message' in contest_data and contest_data['code'] != 200:
+ code = contest_data['code']
+ msg = contest_data['message']
+ logging.critical(f'Failed to retrieve contest, HTTP status code: {code}, message: {msg}')
+ sys.exit(-1)
+
+while not contest_data['start_time']:
+ logging.info(f'Start time unknown - contest delayed.')
+ time_diff = 30
+ spinner = Halo(spinner='dots', text=f'Sleeping for ~{str(round(time_diff,2))}s while waiting for contest start to be set.')
+ spinner.start()
+ time.sleep(time_diff)
+ spinner.stop()
+ contest_data = requests.get(f'{api_url}/contests/{contest}', verify=verify).json()
+
contest_start = datetime.strptime(contest_data['start_time'], '%Y-%m-%dT%H:%M:%S%z').timestamp()
+contest_start_obj = datetime.strptime(contest_data['start_time'], '%Y-%m-%dT%H:%M:%S%z')
contest_duration = (datetime.strptime(contest_data['duration'], '%H:%M:%S.000') - datetime(1900, 1, 1)).total_seconds()
+if args.no_remap_teams:
+ if args.ignore_teamids:
+ logging.critical('Cannot specify --no_remap_teams and --ignore_teamids at the same time.')
+ sys.exit(-1)
+
+ logging.info('Keeping original team IDs.')
+else:
+ # Get the teams from the contest
+ team_data = requests.get(f'{api_url}/contests/{contest}/teams', verify=verify).json()
+ team_ids = [team['id'] for team in team_data if not team['hidden']]
+
+ if not team_ids:
+ logging.critical('Contest has no teams, can\'t submit.')
+ sys.exit(-1)
+
+ logging.info(f'Remapping teams to {len(team_ids)} teams from API.')
+
now = time.time()
orig_contest_duration = 5 * 60 * 60
first_submission_time = 0
-if len(sys.argv) == 4:
- skip_to_submission = int(sys.argv[3])
- submissions = submissions[skip_to_submission:]
+if args.submissionid:
+ skip_to_submission = args.submissionid
+ submissions = list(dropwhile(lambda s: s['id'] != skip_to_submission, submissions))
logging.info(f'Skipped to submission {skip_to_submission}, {len(submissions)} remaining.')
first_submission_time = (datetime.strptime(submissions[0]['contest_time'][:-4], '%H:%M:%S') - datetime(1900, 1, 1)).total_seconds()
orig_contest_duration -= first_submission_time
@@ -65,39 +122,36 @@
logging.info('Contest will be started at '
+ time.strftime('%X %x %Z', time.localtime(contest_start)) + '.')
simulation_speed = orig_contest_duration/contest_duration
+if args.simulation_speed:
+ simulation_speed = float(args.simulation_speed)
logging.info(f'Simulation speed: {simulation_speed}')
-# problems.json is necessary to map from problem id to label.
-problems = json.load(open('problems.json'))
-summary = ', '.join([p['label'] for p in problems])
-logging.info(f'Loaded {len(problems)} problems ({summary}).')
-
-# We will submit under different user accounts, parse all teams out them.
-accounts_tsv = open('accounts.tsv')
-accounts = dict()
-num_teams = 0
-for line in accounts_tsv:
- credentials = line.rstrip('\n').split('\t')
- if credentials[0] != 'team':
- continue
- num_teams += 1
- accounts[num_teams] = (credentials[2], credentials[3])
+if args.internal_data_source:
+ # problems.json is necessary to map from problem id to label.
+ problems = json.load(open('problems.json'))
+ summary = ', '.join([p['label'] for p in problems])
+ logging.info(f'Loaded {len(problems)} problems ({summary}).')
-problem_to_label = dict()
-for problem in problems:
- problem_to_label[problem['id']] = problem['label']
+ problem_to_label = dict()
+ for problem in problems:
+ problem_to_label[problem['id']] = problem['label']
team_problem_team_map = dict()
submissions_api_url = f'{api_url}/contests/{contest}/submissions'
for submission in submissions:
+ if not submission['id']:
+ continue
times = submission['contest_time'].split(':')
- orig_submission_time = int(times[0])*3600 + int(times[1])*60 + float(times[2])
+ hours = int(times[0])
+ minutes = int(times[1])
+ seconds = float(times[2])
+ orig_submission_time = hours*3600 + minutes*60 + seconds
new_submission_time = orig_submission_time/simulation_speed
time_from_start = time.time() - contest_start
time_diff = new_submission_time - time_from_start
if time_diff > 0:
- logging.info(f'Waiting for simulated contest time of {times[0]}:{times[1]}:{times[2]}.')
+ logging.info(f'Waiting for simulated contest time of {hours}:{minutes:02}:{seconds:06.3f}.')
spinner = Halo(spinner='dots', text=f'Sleeping for ~{str(round(time_diff,2))}s')
spinner.start()
while time_diff > 1:
@@ -113,16 +167,21 @@
# all the time.
team_id = submission['team_id']
problem_id = submission['problem_id']
- # TODO: make this configurable or detect it.
- # With internal data source:
- # problem_label = problem_to_label[problem_id]
- # With external data source:
- problem_label = problem_id
- if team_id not in team_problem_team_map:
- team_problem_team_map[team_id] = dict()
- if problem_label not in team_problem_team_map[team_id]:
- team_problem_team_map[team_id][problem_label] = random.randint(1, num_teams)
- team_id = team_problem_team_map[team_id][problem_label]
+ if args.internal_data_source:
+ problem_label = problem_to_label[problem_id]
+ else:
+ problem_label = problem_id
+ if not args.no_remap_teams:
+ if args.ignore_teamids:
+ team_id = random.choice(team_ids)
+ else:
+ if team_id not in team_problem_team_map:
+ team_problem_team_map[team_id] = dict()
+ if problem_label not in team_problem_team_map[team_id]:
+ team_problem_team_map[team_id][problem_label] = random.choice(team_ids)
+ team_id = team_problem_team_map[team_id][problem_label]
+ else:
+ team_id = submission['team_id']
files = []
problem_zip = ZipFile(submission['id'] + ".zip")
@@ -130,21 +189,25 @@
file_tuple = ('code[]', (name, problem_zip.read(name), 'text/plain'))
files.insert(len(files), file_tuple)
first_filename = files[0][1][0]
- username = accounts[team_id][0]
# We don't allow python2 anymore, let's rewrite it as python3 and try our
# best.
language_id = submission['language_id']
if language_id == 'python2':
language_id = 'python3'
- data = {'problem_id': problem_label, 'language_id': language_id}
+
+ data = {
+ 'problem_id': problem_label,
+ 'language_id': language_id,
+ 'team_id': team_id
+ }
if 'entry_point' in submission:
data['entry_point'] = submission['entry_point']
- logging.info(f'Submitting problem {problem_label} ({first_filename}) on behalf of user {username}.')
+ logging.info(f'Submitting problem {problem_label} ({first_filename}) on behalf of team {team_id}.')
r = requests.post(
submissions_api_url,
data = data,
- auth = accounts[team_id],
files = files,
+ verify = verify,
)
if r.status_code == 200:
sid = r.json()['id']
diff --git a/run_html_validation.sh b/run_html_validation.sh
deleted file mode 100755
index 60611f06..00000000
--- a/run_html_validation.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/bin/sh
-#
-# Cronjob script to automatically run HTML validation of webpages
-# on an live, publicly accessible DOMjudge installation.
-
-set -e
-
-#DEBUG=1
-
-# Optionally specify a user and password when the web interface is
-# password protected:
-#USER=jury
-#PASS=passwordhere
-
-LIVESYSTEMDIR=~/system
-LIVEURLPREFIX="https://${USER:+$USER:$PASS@}www.domjudge.org/domjudge/"
-
-VNUCHECKER=~/vnu_html_checker/vnu.jar
-
-[ "$DEBUG" ] && set -x
-quiet()
-{
- if [ "$DEBUG" ]; then
- "$@"
- else
- "$@" > /dev/null 2>&1
- fi
-}
-
-TEMPDIR=$(mktemp -d /tmp/dj_html_validate-XXXXXX)
-
-cd ~
-
-# Validate DOMjudge webpages running from uptodate git checkout
-# (we cannot use a fresh checkout due to missing website config)
-cd $LIVESYSTEMDIR && git pull -q --rebase --autostash
-
-URLS='
-.
-public/
-public/problems.php
-public/team.php?id=2
-team
-team/clarification.php
-team/clarification.php?id=137
-team/problems.php
-team/scoreboard.php
-team/scoreboard.php?categoryid[]=1&filter=filter
-team/submission_details.php?id=998
-team/submission_details.php?id=1163
-team/team.php?id=2
-jury/
-jury/auditlog.php
-jury/balloons.php
-jury/checkconfig.php
-jury/check_judgings.php
-jury/clarification.php
-jury/clarification.php?id=107
-jury/clarifications.php
-jury/config.php
-jury/contest.php?cmd=add
-jury/contest.php?id=2
-jury/contests.php
-jury/edit_source.php?id=1&rank=0
-jury/executables.php
-jury/executable.php?id=c
-jury/genpasswds.php
-jury/impexp.php
-jury/impexp_contestyaml.php
-jury/index.php
-jury/internal_errors.php
-jury/internal_error.php?id=1
-jury/judgehost.php?id=judgehost1
-jury/judgehosts.php?cmd=edit&referrer=judgehosts.php
-jury/judgehosts.php
-jury/judgehost_restriction.php?cmd=add
-jury/judgehost_restriction.php?id=1
-jury/judgehost_restrictions.php
-jury/language.php?cmd=add
-jury/language.php?id=c
-jury/languages.php
-jury/problem.php?id=7
-jury/problem.php?id=7&cmd=edit
-jury/problems.php
-jury/refresh_cache.php
-jury/rejudging.php?id=1
-jury/rejudgings.php
-jury/scoreboard.php
-jury/scoreboard.php?country[]=NLD
-jury/show_source.php?id=1
-jury/show_source.php?id=3
-jury/submission.php?id=1
-jury/submission.php?id=91
-jury/submission.php?id=94
-jury/submissions.php?view[0]
-jury/submissions.php?view[1]
-jury/submissions.php?view[2]
-jury/team.php?id=2
-jury/team.php?id=2&cmd=edit
-jury/team_affiliations.php
-jury/team_affiliation.php?id=1
-jury/team_categories.php
-jury/team_category.php?id=1
-jury/teams.php
-jury/testcase.php?probid=10
-jury/user.php?id=1
-jury/users.php
-api/'
-
-OFS="$IFS"
-IFS='
-'
-
-check_html ()
-{
- set +e
- url="$LIVEURLPREFIX$1"
- TEMP="$TEMPDIR/$(echo "$1" | sed 's!/!_!g').html"
- curl -s -L -g ${USER:+-u$USER:$PASS} "$url" > "$TEMP"
- # Filter out some unfixable errors and normalize file/URL output:
- java -jar $VNUCHECKER "$TEMP" 2>&1 | \
- grep -v 'error: Attribute .*sorttable_customkey.* not allowed on element' | \
- sed -e 's!^"file.*validate-[a-zA-Z0-9]*/!!' \
- -e 's!^\(api\|jury\|team\|public\)_!\1/!' \
- -e 's/\.html":/ /g;s/%3F/?/g;s/%26/\&/g;s/%3D/=/g;s/%5B/[/g;s/%5D/]/g'
- set -e
-}
-
-for i in $URLS ; do
- check_html "$i"
-done
-IFS="$OFS"
-
-[ "$DEBUG" ] || rm -rf "$TEMPDIR"
-
-exit 0
diff --git a/scoreboard_xml2csv.xslt b/scoreboard_xml2csv.xslt
deleted file mode 100644
index e1cbbef3..00000000
--- a/scoreboard_xml2csv.xslt
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/submit_coverity_scan.sh b/submit_coverity_scan.sh
index b0380f49..9f2be1c6 100755
--- a/submit_coverity_scan.sh
+++ b/submit_coverity_scan.sh
@@ -94,7 +94,7 @@ git_commit()
quietfilter()
{
if [ "$QUIET" ]; then
- grep -vE '(^Coverity Build Capture|^Internal version numbers:|^[[:space:]]*$|compilation units \(100%\)|PHP compilation units \([8-9][0-9]%\)|JavaScript compilation units \(9[0-9]%\)|^Looking for translation units|^The cov-build utility completed successfully\.|^Build successfully submitted\.|^\[STATUS\] |^\*+$|^\|[0-9-]+\|$|^\[WARNING\] (Path .* looks like an idir\.|No source file matches in filesystem capture search directory:|Filesystem capture was enabled but yielded no source file matches\.))' || true
+ grep -vE '(^Coverity Build Capture|^Internal version numbers:|^[[:space:]]*$|compilation units \(100%\)|PHP compilation units \([3-9][0-9]%\)|(JavaScript|Python) compilation units \(9[0-9]%\)|^Looking for translation units|^The cov-build utility completed successfully\.|^Build successfully submitted\.|^\[STATUS\] |^\*+$|^\|[0-9-]+\|$|^\[WARNING\] (Path .* looks like an idir\.|No source file matches in filesystem capture search directory:|Filesystem capture was enabled but yielded no source file matches\.)|For more details, please look at:|/cov-int/build-log\.txt|^Attempting to detect unconfigured compilers in build)' || true
else
cat
fi
diff --git a/update_demoweb.sh b/update_demoweb.sh
index aa040a15..fceedc27 100755
--- a/update_demoweb.sh
+++ b/update_demoweb.sh
@@ -19,7 +19,7 @@ make QUIET=1 maintainer-clean
make QUIET=1 MAINT_CXFLAGS='-g -O2 -Wall -fPIE -Wformat -Wformat-security' \
inplace-conf >/dev/null 2>&1 || true
make QUIET=1 inplace-install docs >/dev/null 2>&1
-composer auto-scripts >/dev/null 2>&1
+(cd webapp && composer auto-scripts >/dev/null 2>&1)
# Reset database to known good state:
mysql $MYSQLOPTS "$DBNAME" < domjudge_demo_remove.sql
diff --git a/update_docs.sh b/update_docs.sh
index 19430d46..3784e400 100755
--- a/update_docs.sh
+++ b/update_docs.sh
@@ -5,6 +5,14 @@ JSON="${WEBSERVER_PATH}/versions.json"
TMPDIR=$(mktemp -d -t 'update_docs-XXXXXX')
DOC_REPO="$TMPDIR/domjudge"
+
+cleanup()
+{
+ rm -rf "$TMPDIR"
+}
+
+trap cleanup EXIT
+
git clone https://github.com/DOMjudge/domjudge.git "$DOC_REPO"
for version in $(jq -r -c '.[]' < "${JSON}") ; do
@@ -26,5 +34,3 @@ for version in $(jq -r -c '.[]' < "${JSON}") ; do
cp -r doc/manual/build/html/* "${WEBSERVER_PATH}/${version}/"
)
done
-
-rm -rf "$TMPDIR"
diff --git a/website/.well-known/security.txt b/website/.well-known/security.txt
new file mode 100644
index 00000000..e7261021
--- /dev/null
+++ b/website/.well-known/security.txt
@@ -0,0 +1,2 @@
+Contact: mailto:admin@domjudge.org
+Expires: 2023-11-06T23:00:00.000Z
diff --git a/website/DOMjudgelogo.svg b/website/DOMjudgelogo.svg
index 06e2512c..75dde9b7 100644
--- a/website/DOMjudgelogo.svg
+++ b/website/DOMjudgelogo.svg
@@ -50,7 +50,7 @@
inkscape:label="DOMjudgelogo"
transform="matrix(1.3333333,0,0,-1.3333333,-142.66667,1122.5195)">DOM
DOM
judge
SOCAL (since 2013),
RMRC (since 2014),
South Pacific Regional
-ICPC World Finals (since 2012, in 2018 and 2019 as main system)
+ICPC Championship:
+ NAC (in 2023)
+ICPC World Finals (since 2012, and since 2018 as main system)
DOMjudge has also been adapted for use in numerous online contests, as
@@ -54,7 +56,7 @@ If you are using (an adapted version of) DOMjudge, we would be glad to hear abou
The following is required to run DOMjudge:
At least one machine running Linux, with root access
-Apache (or Nginx) web server with PHP >= 7.1.3 and
+ Apache (or Nginx) web server with PHP >= 7.4.0 and
PHP command line interface
MySQL or MariaDB database server version 5.5.3 or newer
Compilers for the languages you want to support
diff --git a/website/chat.shtml b/website/chat.shtml
index 0ac4213d..c8b4c65b 100644
--- a/website/chat.shtml
+++ b/website/chat.shtml
@@ -11,10 +11,42 @@
this link to sign up .
+
+Netiquette
- Once you have joined the Slack workspace, feel free to ask any DOMjudge-related
- questions you might have. Please do note that it may sometimes take a bit longer
- than a few minutes to get a response, partly because people might be in different timezones.
-
+ Once you have joined the Slack workspace, feel free to ask any
+ DOMjudge-related questions you might have. Please do take some
+ common internet etiquette in consideration:
+
+
+ Don't demand (quick) answers
+
+ We're all volunteers doing this in our free time. Although we
+ do our best to respond to questions, people may not always be
+ available due to timezone differences or simply other reasons.
+
+
+ Ask questions in public channels
+
+ Do not ask questions privately, unless you're invited to or
+ you know that that person knows the answer and this is not
+ relevant to others.
+
+
+ Provide clear details
+
+ When you run into a problem or error, provide as detailed as
+ possible how you got there and any exact errors you got. The
+ more details you provide, the better and quicker we can help.
+
+
+ Prefer text over screenshots
+
+ When possible, copy&paste error messages, configuration
+ data, etc. as code quoted text instead of
+ screenshots. This makes it easier to read on any device and
+ copy&paste.
+
+
diff --git a/website/checktestdata.shtml b/website/checktestdata.shtml
index fe750a0e..c0215a0d 100644
--- a/website/checktestdata.shtml
+++ b/website/checktestdata.shtml
@@ -24,8 +24,8 @@ example grammar scripts for more details.
The checktestdata sources can be found at
GitHub .
To build checktestdata from repository sources requires
-Flexc++ and
-Bisonc++ .
+Flexc++ and
+Bisonc++ .
Alternatively, the release branch from the checktestdata
repository contains pregenerated parser source files, which do not
require Flexc++/Bisonc++. Finally, Debian packages can be installed
diff --git a/website/demo.shtml b/website/demo.shtml
index 27125619..2d463d54 100644
--- a/website/demo.shtml
+++ b/website/demo.shtml
@@ -27,14 +27,28 @@ populated with data from a live contest.
Public interface
Shows the scoreboard and the problemset.
Team interface
-Shows your submissions, submission details, clarifications, the problemset and the scoreboard. Log in with username and password team .
-As this is a demo, you can't actually submit solutions.
+Shows your submissions, submission details, clarifications, the
+problemset and the scoreboard. Log in with username and password team .
+You can submit solutions through the web interface, but as this is a demo,
+these will not get judged. The submit client is also not available.
Jury/admin interface
The full set of options available to judges and administrators.
Log in with username and password jury or username and password admin respectively. The admin interface is a superset of the jury interface with additional options to configure the contests.
There are no judgehosts running, so things like rejudging do not have much effect.
Team source code and testdata in/output has been replaced with a placeholder, so the source formatting
and testdata diff functionality is not useful.
+API documentation
+Documentation on how to use DOMjudge from scripts. This is used by our submit client and the configure-domjudge & import-contest scripts. Also useful if you want to use only parts of DOMjudge, for example as you use your own custom scoreboard or another submission method. Some of the tools also work via this API.
+Contest API
+The contest API itself, which adheres to the Contest API specification .
+Note that we do not implement all optional details of the API, some
+new specifications from the draft version may be work in progress.
+We also have some private additions; additional properties in
+endpoints specified by the standard can be disabled by passing the URL
+parameter strict=1 .
+Various endpoints are available (or provide full access) only with
+admin credentials, which have to be provided via HTTP basic auth;
+username and password are admin .
The data in the system is reset to a known good state every night, so feel
diff --git a/website/development.shtml b/website/development.shtml
index e15ae649..201c2ecf 100644
--- a/website/development.shtml
+++ b/website/development.shtml
@@ -1,12 +1,12 @@
DOMjudge is open source, free software and has an active development
-community with contributors from around the world. Question, suggestion
+community with contributors from around the world. Question, suggestion,
bug or patch? All those are most welcome via the channels below.
Source code
-The DOMjudge sources are maintained
+
The DOMjudge sources are maintained
on GitHub .
You can get the main repository
with the following command:
diff --git a/website/documentation.shtml b/website/documentation.shtml
index f37341eb..83c72ba4 100644
--- a/website/documentation.shtml
+++ b/website/documentation.shtml
@@ -5,9 +5,8 @@
DOMjudge manual
The complete reference for those setting up and running the DOMjudge system as well as the jury.
-Team manual
-Concise manual for participating teams. Only available in Portable
-Document Format (PDF).
+Team manual
+Concise manual for participating teams. Available also as PDF in the DOMjudge tarball.
API documentation
@@ -18,7 +17,7 @@ state as well as perform certain actions, such as submit solutions
and control contest state (with admin role).
The DOMjudge API is an implementation of the ICPC
-Contest API
+Contest API
(except that it does not (yet) implement the optional team-members
endpoint). It also has some extensions, see the
internal API documentation .
diff --git a/website/download.shtml b/website/download.shtml
index 11c08693..e4ee2456 100644
--- a/website/download.shtml
+++ b/website/download.shtml
@@ -54,7 +54,7 @@ The packages are built and tested on Debian stable , but also on newer v
To install these packages, add our
repository GPG key directly to APT with
-curl -o - https://www.domjudge.org/repokey.asc | sudo apt-key add -
+curl -o - https://www.domjudge.org/repokey.asc | sudo tee /etc/apt/trusted.gpg.d/domjudge.org.asc
or first check our signatures on the key for authenticity.
Then add the following lines to /etc/apt/sources.list:
diff --git a/website/header.shtml b/website/header.shtml
index 690f8f53..1f35c97d 100644
--- a/website/header.shtml
+++ b/website/header.shtml
@@ -1,7 +1,7 @@
-
@@ -19,8 +19,9 @@
+ width="80" height="169" id="domjudgeLogo" class="float-end d-none d-md-block">
diff --git a/website/index.shtml b/website/index.shtml
index d53e4b6f..f765dc24 100644
--- a/website/index.shtml
+++ b/website/index.shtml
@@ -1,7 +1,8 @@
-
-
+
+
+
DOMjudge
DOMjudge is an automated system to run programming contests, like the
@@ -11,8 +12,10 @@
system to plug in languages and has a feature-rich interface for the judges.
The system has been used in many live contests and is free, open source
software that you can adapt to your needs.
-
Learn more »
-
+
+
Learn more »
+
+
diff --git a/website/style_domjudge.css b/website/style_domjudge.css
index 37d14eb2..0c592489 100644
--- a/website/style_domjudge.css
+++ b/website/style_domjudge.css
@@ -14,3 +14,7 @@ kbd {
color: inherit;
background-color: transparent;
}
+
+a {
+ text-decoration: none;
+}
diff --git a/website/tools.shtml b/website/tools.shtml
index c81c97ea..822e1ce6 100644
--- a/website/tools.shtml
+++ b/website/tools.shtml
@@ -23,10 +23,18 @@
and testing commands while working on an ICPC-style problem. Ideally one should
never have to manually run any compilation or testing command themselves.
The
Kattis Problem Tool is
- another collection of tools to createproblem packages. DOMjudge is able to
+ another collection of tools to create problem packages. DOMjudge is able to
import this format.
+
Polygon2DOMjudge
+
+
Polygon2DOMjudge
+ is a tool that converts problem packages exported from the
+ Codeforces Polygon Platform
+ into the DOMjudge Problem Package format.
+
+
Contest as a service
Cloudcontest
@@ -45,8 +53,9 @@
ICPC Tools
ICPC Tools is a set of (free to use)
- tools that can work with a CLICS compatible API, like DOMjudge provides. It has a
- scoreboard display and resolver, balloons utility and various other presentation tools.
+ tools that can work with a
+ CLICS compatible API , like DOMjudge provides. It has a scoreboard display and
+ resolver, balloons utility and various other presentation tools.
DOMjura
@@ -69,4 +78,16 @@
is useful to keep problem data secret and to make sharing credentials easier on the organizer.
+
DOMjudge for teaching
+
+
DOMjudge is mostly focused on being a contest system for short running contests. Some people use the
+automatic judging for teaching. When possible we can incorporate such extensions to provide easier access
+through the API and UI with feature requests. Since not everything will make it into our releases, we link here to
+some extensions to help with this use case.
+
+Isolate this is an alternative to our judgedaemon with chroot.
+DOMtutor
+
+
+