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;
}
static struct PyMethodDef BuildService_methods[] = {
+ {
+ "job_finished",
+ (PyCFunction)BuildService_job_finished,
+ METH_VARARGS|METH_KEYWORDS,
+ NULL,
+ },
{
"submit_stats",
(PyCFunction)BuildService_submit_stats,
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;
+}
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 */
pakfire_buildservice_delete_repo;
pakfire_buildservice_get_url;
pakfire_buildservice_submit_stats;
+ pakfire_buildservice_job_finished;
# dependencies
pakfire_static_version_compare;
import kerberos
import logging
import os
+
import subprocess
import tempfile
import tornado.httpclient
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):
"""