From 483c9264269d96ff25fde13110500e7fc422d51a Mon Sep 17 00:00:00 2001
From: Marco Clemencic <marco.clemencic@cern.ch>
Date: Tue, 23 Apr 2024 16:21:24 +0200
Subject: [PATCH] Record periodic tests to start in CouchDB

drop the dependency on lbmessaging
---
 lb/nightly/frontend/periodic_tests.py         | 65 +++++++++++++------
 .../frontend/templates/periodic_tests.html    | 15 ++++-
 poetry.lock                                   | 31 +--------
 pyproject.toml                                |  1 -
 4 files changed, 59 insertions(+), 53 deletions(-)

diff --git a/lb/nightly/frontend/periodic_tests.py b/lb/nightly/frontend/periodic_tests.py
index 277817b..ab2ef72 100644
--- a/lb/nightly/frontend/periodic_tests.py
+++ b/lb/nightly/frontend/periodic_tests.py
@@ -1,15 +1,14 @@
 from datetime import date, timedelta
 
 from flask import flash, redirect, render_template, request, url_for
-from lbmessaging.exchanges.Common import check_channel, get_connection
-from lbmessaging.exchanges.PeriodicTestsExchange import PeriodicTestsExchange
 from requests import HTTPError
 from werkzeug.exceptions import BadRequest
 
 from .application import app, require_roles
 from .models import get_periodic_tests, get_slots_since_day, legacy_db
 
-_DOC_NAME = "frontend:periodic-configuration"
+_CONFIGURATIONS_DOC_NAME = "frontend:periodic-configuration"
+_TEST_REQUESTS_DOC_NAME = "frontend:tests-to-start"
 
 
 def _db():
@@ -50,17 +49,25 @@ def start_periodic_test():
 @require_roles("builder")
 def start_periodic_test_trigger():
     try:
-        configs = _db()[_DOC_NAME]["configurations"]
+        configs = _db()[_CONFIGURATIONS_DOC_NAME]["configurations"]
         fields = dict(request.form)
 
-        channel = check_channel(get_connection())
-        broker = PeriodicTestsExchange(channel)
+        doc = _db().create_document(
+            {
+                "_id": _TEST_REQUESTS_DOC_NAME,
+                "type": "periodic-tests-requested",
+                "requests": [],
+            }
+        )
+        if "_rev" not in doc:
+            # document already in the database, fetch the most recent version
+            doc.fetch()
 
         for pk in [int(key) for key in request.form.getlist("group_env")]:
             config = [entry for entry in configs if pk == entry["pk"]][0]
             fields.update(config)
-            args = [
-                fields[key]
+            args = {
+                key: fields[key]
                 for key in (
                     "slot",
                     "build_id",
@@ -71,15 +78,31 @@ def start_periodic_test_trigger():
                     "runner",
                     "os_label",
                 )
-            ]
-
-            broker.request_test(*args)
-            flash(
-                "'{group} : {env}' for {project} {platform} in {slot}/{build_id}".format(
-                    **fields
-                ),
-                "started_tests",
-            )
+            }
+            # we give ourselves 5 attempts to resolve conflicts (we never need more than one)
+            for _attempt in range(5):
+                try:
+                    doc["requests"].push(args)
+                    doc.save()
+                    flash(
+                        "'{group} : {env}' for {project} {platform} in {slot}/{build_id}".format(
+                            **fields
+                        ),
+                        "started_tests",
+                    )
+                    break
+                except HTTPError as err:
+                    if "conflict" not in str(err).lower():
+                        raise
+                    doc.fetch()
+            else:
+                flash(
+                    "'{group} : {env}' for {project} {platform} in {slot}/{build_id}".format(
+                        **fields
+                    ),
+                    "started_tests_failures",
+                )
+
     except KeyError:
         raise BadRequest()
 
@@ -94,7 +117,7 @@ def periodic_configs():
         if request.form["action"] in ("remove", "add"):
             doc = _db().create_document(
                 {
-                    "_id": _DOC_NAME,
+                    "_id": _CONFIGURATIONS_DOC_NAME,
                     "type": "periodic-configuration",
                     "configurations": [],
                 }
@@ -166,6 +189,8 @@ def periodic_configs():
             # neither add nor remove, let's pretend it's a GET request
             pass
 
-    if _DOC_NAME in _db():
-        return {"configurations": _db()[_DOC_NAME].get("configurations", [])}
+    if _CONFIGURATIONS_DOC_NAME in _db():
+        return {
+            "configurations": _db()[_CONFIGURATIONS_DOC_NAME].get("configurations", [])
+        }
     return {"configurations": []}
diff --git a/lb/nightly/frontend/templates/periodic_tests.html b/lb/nightly/frontend/templates/periodic_tests.html
index 5995dad..81e73e1 100644
--- a/lb/nightly/frontend/templates/periodic_tests.html
+++ b/lb/nightly/frontend/templates/periodic_tests.html
@@ -12,7 +12,7 @@
 {% block main_content %}
 {% with messages = get_flashed_messages(category_filter=["started_tests"]) %}
 {% if messages %}
-<div class="flashes">
+<div class="flashes text-primary">
     Started tests:
     <ul>
         {% for message in messages %}
@@ -21,6 +21,17 @@
     </ul>
 </div>
 {% endif %}
+{% with messages = get_flashed_messages(category_filter=["started_tests_failures"]) %}
+{% if messages %}
+<div class="flashes text-danger">
+    Failed to start tests:
+    <ul>
+        {% for message in messages %}
+        <li>{{ message }}</li>
+        {% endfor %}
+    </ul>
+</div>
+{% endif %}
 {% endwith %}
 
 {%- if "builder" in session.get("roles", []) -%}
@@ -140,6 +151,6 @@
     });
     $("#dataTable").show();
 
-    $(".flashes").delay(10000).hide(300);
+    $(".flashes.text-primary").delay(10000).hide(300);
 </script>
 {% endblock %}
diff --git a/poetry.lock b/poetry.lock
index ae5d1d9..c692cb4 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -934,19 +934,6 @@ lbplatformutils = ">=4.3.6,<5.0.0"
 mysqlclient = ">=2.0.3,<3.0.0"
 requests = ">=2.27.1,<3.0.0"
 
-[[package]]
-name = "lbmessaging"
-version = "1.2.4"
-description = "LHCb devtools communication bus"
-optional = false
-python-versions = "*"
-files = [
-    {file = "lbmessaging-1.2.4.tar.gz", hash = "sha256:2491807443ac2e335c0ae6447f5b0980d4e2a3162070d7644a4e0f31fbd4d0fe"},
-]
-
-[package.dependencies]
-pika = "*"
-
 [[package]]
 name = "lbplatformutils"
 version = "4.4.3"
@@ -1089,22 +1076,6 @@ files = [
     {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
 ]
 
-[[package]]
-name = "pika"
-version = "1.3.2"
-description = "Pika Python AMQP Client Library"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f"},
-    {file = "pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f"},
-]
-
-[package.extras]
-gevent = ["gevent"]
-tornado = ["tornado"]
-twisted = ["twisted"]
-
 [[package]]
 name = "platformdirs"
 version = "4.2.0"
@@ -1833,4 +1804,4 @@ socks = []
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.9"
-content-hash = "58e66edb0c9aff005f1375e08b888281279b26fca956e4bcb9979ca58b7cda1f"
+content-hash = "2ebe0ad13865a0ad4794fbe67fd009541fec4ec106f96f4dc309fc646f1b0629"
diff --git a/pyproject.toml b/pyproject.toml
index 290cfce..c89945a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,7 +23,6 @@ gunicorn = {version = "^20.1.0", optional = true}
 lb-nightly-configuration = "^0.3.2"
 lb-nightly-db = "^0.2"
 lb-nightly-rpc = "^0.2"
-lbmessaging = "^1.2.4"
 python = "^3.9"
 python-dateutil = "^2.8.2"
 python-gitlab = "^3.4.0"
-- 
GitLab