Skip to content
Snippets Groups Projects
Unverified Commit 79b85ab9 authored by Mathieu Othacehe's avatar Mathieu Othacehe
Browse files

Add "/api/jobs/history" API.

* src/cuirass/database.scm (db-get-jobs-history): New procedure.
* src/cuirass/http.scm (jobs-history->json-object): New procedure.
(url-handler): New "/api/jobs/history" route.
* tests/database.scm ("db-get-jobs-history"): New test.
parent 4a9f82ef
No related branches found
No related tags found
No related merge requests found
......@@ -932,6 +932,49 @@ The job name, as a string.
@end table
@subsection Jobs history
The history of jobs across the last evaluations of a given
specification can be optained with the API "/api/jobs/history".
This request accepts three mandatory parameters.
@table @code
@item specification
The specification name. This parameter is @emph{mandatory}.
@item names
Filter query result to jobs which names are part of the given
@code{names} list, a comma separated list of job names. This
parameter is @emph{mandatory}.
@item nr
Limit query result to nr elements. This parameter is @emph{mandatory}.
@end table
For example, to ask for the history of @code{emacs.x86_64-linux} and
@code{emacs-minimal.x86_64-linux} jobs of the @code{master}
specification over the last 10 evaluations:
@example
$ curl "http://localhost:8080/api/jobs/history?spec=master&names=emacs.x86_64-linux,emacs-minimal.x86_64-linux&nr=10"
@end example
The nominal output is a JSON array which objects have the following field:
@table @code
@item evaluation
The unique evaluation id.
@item checkouts
The evaluation checkouts as a JSON array.
@item name
The jobs list for this evaluation, as a JSON array.
@end table
@subsection Latest builds
The list of latest builds can be obtained with the API
......@@ -1117,12 +1160,18 @@ This integer field references the evaluation identifier from the
@code{Evaluations} table, indicating to which evaluation this job
belongs.
@item builds
@item build
This integer field references the build id from the @code{Builds}
table, corresponding to this job.
@item status
This integer field holds the build status of the above build. The
goal of this duplication is to avoid expensive joins between the Jobs
and Builds table.
@item system
This text field holds the system name of the derivation.
This text field holds the system name of the above build. This field
is duplicated for the same reason as the above status field.
@end table
......
......@@ -69,6 +69,7 @@
db-get-time-since-previous-eval
db-get-build-percentages
db-get-jobs
db-get-jobs-history
db-register-builds
db-update-build-status!
db-update-build-worker!
......@@ -737,6 +738,50 @@ ORDER BY Jobs.name")
(#:name . ,name))
jobs))))))))
(define* (db-get-jobs-history names #:key spec limit)
"Return the list of jobs from Jobs table which name is a member of the given
NAMES list, for the last LIMIT evaluations of SPEC specification."
(define (format-names names)
(format #f "{~a}" (string-join names ",")))
(with-db-worker-thread db
(let ((query "
SELECT name, evaluation, build, status FROM Jobs
WHERE Jobs.evaluation IN (SELECT id FROM Evaluations
WHERE specification = :spec AND status = 0
ORDER BY id DESC LIMIT :nr)
AND Jobs.name = ANY(:names);")
(params
`((#:spec . ,spec)
(#:names . ,(format-names names)))))
(let loop ((rows (exec-query/bind-params db query params))
(evaluations '()))
(match rows
(() (sort evaluations
(lambda (a b)
(let ((eval (cut assq-ref <> #:evaluation)))
(> (eval a) (eval b))))))
(((name evaluation build status)
. rest)
(loop rest
(let* ((eval (find (lambda (e)
(eq? (assq-ref e #:evaluation)
(string->number evaluation)))
evaluations))
(jobs (and eval
(assq-ref eval #:jobs)))
(job `((#:name . ,name)
(#:build . ,(string->number build))
(#:status . ,(string->number status)))))
(if eval
(begin
(assq-set! eval #:jobs (cons job jobs))
evaluations)
(cons `((#:evaluation . ,(string->number evaluation))
(#:checkouts . ,(db-get-checkouts evaluation))
(#:jobs . ,(list job)))
evaluations))))))))))
(define (db-register-builds jobs eval-id specification)
(define (new-outputs? outputs)
(let ((new-outputs
......
......@@ -173,6 +173,18 @@
(#:systems . ,(list->vector
(specification-systems spec)))))
(define (jobs-history->json-object history)
"Turn HISTORY into a representation suitable for 'json->scm'."
(object->json-string
(list->vector
(map (lambda (eval)
`((#:evaluation . ,(assq-ref eval #:evaluation))
(#:checkouts . ,(list->vector
(assq-ref eval #:checkouts)))
(#:jobs . ,(list->vector
(assq-ref eval #:jobs)))))
history))))
(define (handle-build-request build-id)
"Retrieve build identified by BUILD-ID over the database and convert it to
hydra format. Return #f is not build was found."
......@@ -705,6 +717,27 @@ into a specification record and return it."
(cut string-split <> #\,)))
,@params)))))
(respond-json-with-error 500 "Parameter not defined!"))))
(('GET "api" "jobs" "history")
(let* ((params (request-parameters request))
(names (and=> (assq-ref params 'names)
(cut string-split <> #\,)))
(spec (assq-ref params 'spec))
(limit (assq-ref params 'nr)))
(cond
((not (and names spec limit))
(respond-json-with-error 500 "Parameter not defined"))
((> limit 100)
(respond-json-with-error 500 "Maximum limit exceeded"))
(else
(catch 'json-invalid
(lambda ()
(respond-json
(jobs-history->json-object
(db-get-jobs-history names
#:spec spec
#:limit limit))))
(lambda _
(respond-json-with-error 500 "Invalid body")))))))
(('GET "api" "evaluation")
(let* ((params (request-parameters request))
(id (assq-ref params 'id)))
......
......@@ -235,6 +235,16 @@ timestamp, checkouttime, evaltime) VALUES ('guix', 0, 0, 0, 0);")
#:derivation)
"/test.drv"))))
(test-assert "db-get-jobs-history"
(begin
(db-set-evaluation-status 4 (evaluation-status succeeded))
(match (db-get-jobs-history '("test")
#:spec "guix"
#:limit 2)
((eval)
(and (eq? (assq-ref eval #:evaluation) 4)
(eq? (length (assq-ref eval #:jobs)) 1))))))
(test-assert "db-update-build-status!"
(db-update-build-status! "/test.drv"
(build-status failed)))
......@@ -342,7 +352,7 @@ timestamp, checkouttime, evaltime) VALUES ('guix', 0, 0, 0, 0);")
(db-get-evaluations-id-max "foo"))
(test-equal "db-get-latest-evaluations"
1
4
(match (db-get-latest-evaluations)
((eval)
(assq-ref eval #:evaluation))))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment