.. _api: HTTP REST API v1 ================= Authentication -------------- Authentication is based on secret tokens. In order to use the A||GO HTTP API, you need to copy your **private token** from `your profile page `_. All requests must be include an **Authorization** header: .. sourcecode:: http Authorization: Token token= .. _api-create: Post a job ---------- .. http:post:: /api/v1/jobs Submit a new job :form job[webapp]: *(required)* the short name of the webapp (as it appears in urls) :form job[version]: the version of the webapp to be used :form job[queue]: job queue (for the scheduler) :form job[param]: command-line arguments for the job :form files[0]: a first file :form files[1]: a second file, etc ... :reqheader Authorization: Token token= :reqheader Content-Type: multipart/form-data **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id":40155, "url":"https://allgo18.inria.fr/api/v1/jobs/40155" "avg_time": 0 } Here is a example with curl .. code-block:: bash curl -H 'Authorization: Token token=' \ -X POST -F job[webapp]='' -F job[param]='arg1 arg2' \ -F files[0]='@' -F files[1]='@' \ https://allgo18.inria.fr/api/v1/jobs and with python requests: .. code-block:: python3 requests.post("https://allgo18.inria.fr/api/v1/jobs", headers={"Authorization": "Token token="}, data={"job[webapp]": "", "job[param]": "arg1 arg2"}, files={"files[0]": open("", "rb"), "files[1]": open("", "rb")}) This request will return you, either the errors, or if it's ok, your **job id** and **job url** .. _api-monitor: Monitor a job ------------- .. http:get:: /api/v1/jobs/(int: job_id)/events :query offset: optional starting offset (in bytes) for streaming the logs (default is 0). Use -1 to disable the logs. :reqheader Authorization: Token token= This endpoint streams a sequence of `line-delimited json objects `_ to report the progress of the job. The current implementation produces four kind of messages: - **Status**: reports the new state of the job .. code-block:: json {"status": ""} - **Logs**: streams the console output of the job (allgo.log) .. code-block:: json {"logs": ""} EOF on the console is signaled with an empty *logs* message: .. code-block:: json {"logs": ""} - **EOF**: sent at the end of the stream .. code-block:: json {"eof": null} - **Keepalive**: empty messages sent periodically to ensure that the connection is alive .. code-block:: json {} **Notes** - The API may be extended in the future. Any unknown kind of message should be ignored by the client. - The stream ends when the job is terminated and the end of logs is reached. An **eof** message is sent to mark the end of the stream. If the stream terminates without the **eof** message, then you should assume that it was prematurely terminated. You may consider making a new request to resume the stream (possibly with a positive *offset* value to resume the logs where it was interrupted), **but please wait at least a few seconds before making the new request (to avoid overloading the server in case your app enters an infinite loop)**. - States changes and logs are streamed independently of each other, therefore you may receive logs even after the job is reported as terminated. Only the **eof** message guarantees that the stream is terminated. - If your need is only to be notified when the job terminates (you are not interested in processing a stream of json object), then you should just make a minimal request (without the logs) ``GET /api/v1/jobs//events?offset=-1`` and ignore the content of the response. The request will finish when the job terminates. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "status": "waiting" } {} {} { "status": "running" } { "logs": "some output\n" } {} { "logs": "some other output\n" } { "status": "done" } { "logs": "end of the output\n" } ( "logs": "" } { "eof": null } Get the results --------------- Get the result of the job and the output files with: .. http:get:: /api/v1/jobs/(int: job_id) Retrieve job informations :reqheader Authorization: Token token= **Example**: .. http:example:: curl python-requests GET /api/v1/jobs/40155 HTTP/1.1 Host: allgo18.inria.fr Accept: application/json Authorization: Token token= .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "40155": {"conv1_samusa.txt":"https://allgo18.inria.fr/datastore/6/1/0fe2bc68b835e9a1f681e38d5e87001ef955e345/conv1_samusa.txt", "conv1.json":"https://allgo18.inria.fr/datastore/6/1/0fe2bc68b835e9a1f681e38d5e87001ef955e345/conv1.json", "conv1.mp3":"https://allgo18.inria.fr/datastore/6/1/0fe2bc68b835e9a1f681e38d5e87001ef955e345/conv1.mp3", "allgo.log":"https://allgo18.inria.fr/datastore/6/1/0fe2bc68b835e9a1f681e38d5e87001ef955e345/allgo.log" }, "status":"done" } Each file could be downloaded with the link associated. .. _api-abort: Abort a job ----------- .. http:post:: /api/v1/jobs/(int: job_id)/abort :reqheader Authorization: Token token= Jobs may be aborted at any time. The abort process is asynchronous and the response is sent immediately. The app first receives a SIGTERM to let it terminate cleanly, then after a grace period (10 seconds by default) a SIGKILL is sent. You should use the :ref:`events` API endpoint if you need to wait until the job is terminated. This operation is idempotent. Calling it on a job which is already terminated or aborting will return HTTP 200. **Example responses**: .. sourcecode:: http HTTP/1.1 200 OK {"info": "aborting job"} .. sourcecode:: http HTTP/1.1 200 OK {"info": "job already terminated"} .. _api-delete: Delete a job ------------ .. http:delete:: /api/v1/jobs/(int: job_id) :reqheader Authorization: Token token= Running jobs cannot be deleted, they first need to be aborted. This operation is idempotent. Calling it on a job which is already deleted will return HTTP 200. **Example responses**: .. sourcecode:: http HTTP/1.1 200 OK {"info": "job successfully deleted"} .. sourcecode:: http HTTP/1.1 409 Conflict {"error": "cannot delete a running job"} .. External client --------------- | You can use the API with a client, that could simplify the build of sophisticated scripts. | As an example, we provide a fully documented `python client`_, available from pip [soon]. .. _`python client`: https://allgo.gitlabpages.inria.fr/api-clients/python_client/ Get metrics ----------- We provide a way to get simple metrics about *your* applications. It allows to get for a given webapp, the number of job over a period of time. .. http:get:: /api/v1/metrics/(string: metric_type)/(int: webapp_id) We provide 4 metrics type: - **per_user** : the number of jobs per time period and per user. - **per_result** : the number of jobs per time period and per result. - **created** : the number of jobs created per time period. - **all** : all jobs created per time period. 3 parameters can refine your request : - **step** : string [*year,month,week,day*], the step of aggregation. By default set to 'day' - **from** : date, the begin (including) of the time period to interrogate. By default set to 'first result' - **to** : date, the end (excluding) of the time period to interrogate. By default set to 'last result' *Note: the dates should have the format "%Y-%m-%d", so 2019-04-01 for the first of april 2019.* :reqheader Authorization: Token token= .. http:example:: curl wget httpie python-requests GET /api/v1/metrics/per_user/151?from=2019-01-01&step=day HTTP/1.1 Host: allgo18.inria.fr Accept: application/json Authorization: Token token= **Example response**: .. sourcecode:: http { "GATB-Compiler": { "from": "01-01-2017 00:00", "to": "01-01-2018 00:00", "data": [ { "time_period": "2017-01-01T00:00:00Z", "result": "ERROR", "n": 2499 }, { "time_period": "2017-01-01T00:00:00Z", "result": "SUCCESS", "n": 22 }, ... ] } }