]> git.ipfire.org Git - people/ms/pakfire.git/commitdiff
buildservice: Add function to signal finished build jobs
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 1 Nov 2023 09:14:28 +0000 (09:14 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 1 Nov 2023 09:14:28 +0000 (09:14 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/_pakfire/buildservice.c
src/libpakfire/buildservice.c
src/libpakfire/include/pakfire/buildservice.h
src/libpakfire/libpakfire.sym
src/pakfire/buildservice.py

index 9a12632069a212432c4a9f8b139233376e1889fa..67a401be06b68384e2938e461d7ef0ab93660c2a 100644 (file)
@@ -69,6 +69,106 @@ static PyObject* BuildService_get_url(BuildServiceObject* self) {
        return PyUnicode_FromString(url);
 }
 
+static int convert_packages(PyObject* object, void* data) {
+       char*** packages = data;
+
+       // Called for cleanup
+       if (!object)
+               goto ERROR;
+
+       // Nothing to do when object is None
+       if (object == Py_None)
+               return Py_CLEANUP_SUPPORTED;
+
+       if (!PySequence_Check(object)) {
+               PyErr_SetString(PyExc_ValueError, "packages must be a sequence");
+               goto ERROR;
+       }
+
+       const unsigned int length = PySequence_Length(object);
+       if (!length)
+               return Py_CLEANUP_SUPPORTED;
+
+       // Allocate array
+       *packages = calloc(length + 1, sizeof(*packages));
+       if (!*packages) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
+
+       for (unsigned int i = 0; i < length; i++) {
+               PyObject* item = PySequence_GetItem(object, i);
+
+               // Check if input is a string
+               if (!PyUnicode_Check(item)) {
+                       Py_DECREF(item);
+
+                       PyErr_SetString(PyExc_AttributeError, "Expected a string");
+                       goto ERROR;
+               }
+
+               // Fetch string
+               const char* package = PyUnicode_AsUTF8(item);
+               if (!package) {
+                       Py_DECREF(item);
+                       goto ERROR;
+               }
+
+               // Add package to array
+               (*packages)[i] = strdup(package);
+               if (!(*packages)[i]) {
+                       Py_DECREF(item);
+                       goto ERROR;
+               }
+
+               Py_DECREF(item);
+       }
+
+       // Success
+       return Py_CLEANUP_SUPPORTED;
+
+ERROR:
+       if (*packages) {
+               for (char** package = *packages; *package; package++)
+                       free(*package);
+               free(*packages);
+       }
+
+       return 0;
+}
+
+static PyObject* BuildService_job_finished(BuildServiceObject* self, PyObject* args, PyObject* kwargs) {
+       char* kwlist[] = { "uuid", "success", "logfile", "packages", NULL };
+       const char* uuid = NULL;
+       int success = 0;
+       const char* logfile = NULL;
+       char** packages = NULL;
+       int r;
+
+       // Parse arguments
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sp|zO&", kwlist, &uuid, &success,
+                       &logfile, convert_packages, &packages))
+               return NULL;
+
+       // Send request
+       r = pakfire_buildservice_job_finished(self->service, uuid, success,
+               logfile, (const char**)packages);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (packages) {
+               for (char** package = packages; *package; package++)
+                       free(*package);
+               free(packages);
+       }
+
+       if (r)
+               return NULL;
+
+       Py_RETURN_NONE;
+}
+
 static PyObject* BuildService_submit_stats(BuildServiceObject* self) {
        int r;
 
@@ -116,6 +216,12 @@ ERROR:
 }
 
 static struct PyMethodDef BuildService_methods[] = {
+       {
+               "job_finished",
+               (PyCFunction)BuildService_job_finished,
+               METH_VARARGS|METH_KEYWORDS,
+               NULL,
+       },
        {
                "submit_stats",
                (PyCFunction)BuildService_submit_stats,
index 66d9816af2d9cfe3ec5d95e8c421636f424ebd64..1634e7a72afb126aea037511794a82f809e3f454 100644 (file)
@@ -1216,3 +1216,81 @@ ERROR:
 
        return r;
 }
+
+PAKFIRE_EXPORT int pakfire_buildservice_job_finished(struct pakfire_buildservice* service,
+               const char* uuid, int success, const char* logfile, const char** packages) {
+       struct pakfire_xfer* xfer = NULL;
+       char url[PATH_MAX];
+       char* buffer = NULL;
+       size_t length = 0;
+       int r;
+
+       struct json_object* response = NULL;
+
+       unsigned int num_packages = 0;
+
+       // Count packages
+       for (const char** package = packages; *package; packages++)
+               num_packages++;
+
+       // Compose the URL
+       r = pakfire_string_format(url, "/api/v1/jobs/%s", uuid);
+       if (r)
+               goto ERROR;
+
+       // Create a new xfer
+       r = pakfire_buildservice_create_xfer(&xfer, service, url);
+       if (r)
+               goto ERROR;
+
+       // Enable authentication
+       r = pakfire_xfer_auth(xfer);
+       if (r)
+               goto ERROR;
+
+       // Has the job been successful?
+       r = pakfire_xfer_add_param(xfer, "success", "%s", (success) ? "true" : "false");
+       if (r)
+               goto ERROR;
+
+       // Logfile
+       if (logfile) {
+               r = pakfire_xfer_add_param(xfer, "logfile", "%s", logfile);
+               if (r)
+                       goto ERROR;
+       }
+
+       // Packages
+       for (const char** package = packages; *package; package++) {
+               r = pakfire_xfer_add_param(xfer, "package", "%s", *package);
+               if (r)
+                       goto ERROR;
+       }
+
+       // Write the response to the buffer
+       r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
+       if (r)
+               goto ERROR;
+
+       // Run the xfer
+       r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
+       if (r)
+               goto ERROR;
+
+       // Parse the response
+       r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
+       if (r) {
+               CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
+               goto ERROR;
+       }
+
+ERROR:
+       if (xfer)
+               pakfire_xfer_unref(xfer);
+       if (response)
+               json_object_put(response);
+       if (buffer)
+               free(buffer);
+
+       return r;
+}
index d705aad18840e57aae8b51352e4b292a86099c66..af11bc6b3ab3684c1dcc556ac2c2d1fbb606eee2 100644 (file)
@@ -67,4 +67,9 @@ int pakfire_buildservice_delete_repo(struct pakfire_buildservice* service,
 
 int pakfire_buildservice_submit_stats(struct pakfire_buildservice* service);
 
+// Jobs
+
+int pakfire_buildservice_job_finished(struct pakfire_buildservice* service,
+       const char* uuid, int success, const char* logfile, const char** packages);
+
 #endif /* PAKFIRE_BUILDSERVICE_H */
index fd416cd102ef37d0ea14f06596eb75d2c0e9a9fe..73fb3968d1a522e006daa597c240332c7ea610f0 100644 (file)
@@ -98,6 +98,7 @@ global:
        pakfire_buildservice_delete_repo;
        pakfire_buildservice_get_url;
        pakfire_buildservice_submit_stats;
+       pakfire_buildservice_job_finished;
 
        # dependencies
        pakfire_static_version_compare;
index f05acfd6b2ed58e3768be75848167370e9418488..08f9d5f87f3a6dc9b6a6bc200671b3fe8c395b04 100644 (file)
@@ -23,6 +23,7 @@ import json
 import kerberos
 import logging
 import os
+
 import subprocess
 import tempfile
 import tornado.httpclient
@@ -429,22 +430,8 @@ class JobControlConnection(Connection):
                        for package in packages:
                                await self.service.upload(package)
 
-               while True:
-                       try:
-                               # Send the request
-                               response = await self.service._request("POST", "/api/v1/jobs/%s/finished" % self.id,
-                                       success="1" if success else "0", logfile=logfile, packages=packages,
-                               )
-
-                       # Try again after a short moment on connection errors
-                       except TemporaryConnectionError as e:
-                               await asyncio.sleep(5)
-
-                       else:
-                               break
-
-               # Handle the response
-               # XXX TODO
+               # Send request
+               self.service.job_finished(self.id, success=success, logfile=logfile, packages=packages)
 
        async def log(self, timestamp, level, message):
                """