diff --git a/src/pylorax/api/v0.py b/src/pylorax/api/v0.py
index 81dc04ff..80834f4c 100644
--- a/src/pylorax/api/v0.py
+++ b/src/pylorax/api/v0.py
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2017 Red Hat, Inc.
+# Copyright (C) 2017-2018 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -14,6 +14,605 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
+""" Setup v0 of the API server
+
+v0_api() must be called to setup the API routes for Flask
+
+Status Responses
+----------------
+
+Some requests only return a status/error response.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+ Example response::
+
+ {
+ "status": true
+ }
+
+ Error response::
+
+ {
+ "error": {
+ "msg": "ggit-error: Failed to remove entry. File isn't in the tree - jboss.toml (-1)"
+ },
+ "status": false
+ }
+
+API Routes
+----------
+
+All of the recipes routes support the optional `branch` argument. If it is not
+used then the API will use the `master` branch for recipes. If you want to create
+a new branch use the `new` or `workspace` routes with ?branch= to
+store the new recipe on the new branch.
+
+`/api/v0/test`
+^^^^^^^^^^^^^^
+
+ Return a test string. It is not JSON encoded.
+
+`/api/v0/status`
+^^^^^^^^^^^^^^^^
+ Return the status of the API Server::
+
+ { "api": "0",
+ "build": "devel",
+ "db_supported": false,
+ "db_version": "0",
+ "schema_version": "0" }
+
+`/api/v0/recipes/list`
+^^^^^^^^^^^^^^^^^^^^^^
+
+ List the available recipes::
+
+ { "limit": 20,
+ "offset": 0,
+ "recipes": [
+ "atlas",
+ "development",
+ "glusterfs",
+ "http-server",
+ "jboss",
+ "kubernetes" ],
+ "total": 6 }
+
+`/api/v0/recipes/info/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return the JSON representation of the recipe. This includes 3 top level
+ objects. `changes` which lists whether or not the workspace is different from
+ the most recent commit. `recipes` which lists the JSON representation of the
+ recipe, and `errors` which will list any errors, like non-existant recipes.
+
+ Example::
+
+ {
+ "changes": [
+ {
+ "changed": false,
+ "name": "glusterfs"
+ }
+ ],
+ "errors": [],
+ "recipes": [
+ {
+ "description": "An example GlusterFS server with samba",
+ "modules": [
+ {
+ "name": "glusterfs",
+ "version": "3.7.*"
+ },
+ {
+ "name": "glusterfs-cli",
+ "version": "3.7.*"
+ }
+ ],
+ "name": "glusterfs",
+ "packages": [
+ {
+ "name": "2ping",
+ "version": "3.2.1"
+ },
+ {
+ "name": "samba",
+ "version": "4.2.*"
+ }
+ ],
+ "version": "0.0.6"
+ }
+ ]
+ }
+
+ Error example::
+
+ {
+ "changes": [],
+ "errors": [
+ {
+ "msg": "ggit-error: the path 'missing.toml' does not exist in the given tree (-3)",
+ "recipe": "missing"
+ }
+ ],
+ "recipes": []
+ }
+
+`/api/v0/recipes/changes/[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return the commits to a recipe. By default it returns the first 20 commits, this
+ can be changed by passing `offset` and/or `limit`. The response will include the
+ commit hash, summary, timestamp, and optionally the revision number. The commit
+ hash can be passed to `/api/v0/recipes/diff/` to retrieve the exact changes.
+
+ Example::
+
+ {
+ "errors": [],
+ "limit": 20,
+ "offset": 0,
+ "recipes": [
+ {
+ "changes": [
+ {
+ "commit": "e083921a7ed1cf2eec91ad12b9ad1e70ef3470be",
+ "message": "Recipe glusterfs, version 0.0.6 saved.",
+ "revision": null,
+ "timestamp": "2017-11-23T00:18:13Z"
+ },
+ {
+ "commit": "cee5f4c20fc33ea4d54bfecf56f4ad41ad15f4f3",
+ "message": "Recipe glusterfs, version 0.0.5 saved.",
+ "revision": null,
+ "timestamp": "2017-11-11T01:00:28Z"
+ },
+ {
+ "commit": "29b492f26ed35d80800b536623bafc51e2f0eff2",
+ "message": "Recipe glusterfs, version 0.0.4 saved.",
+ "revision": null,
+ "timestamp": "2017-11-11T00:28:30Z"
+ },
+ {
+ "commit": "03374adbf080fe34f5c6c29f2e49cc2b86958bf2",
+ "message": "Recipe glusterfs, version 0.0.3 saved.",
+ "revision": null,
+ "timestamp": "2017-11-10T23:15:52Z"
+ },
+ {
+ "commit": "0e08ecbb708675bfabc82952599a1712a843779d",
+ "message": "Recipe glusterfs, version 0.0.2 saved.",
+ "revision": null,
+ "timestamp": "2017-11-10T23:14:56Z"
+ },
+ {
+ "commit": "3e11eb87a63d289662cba4b1804a0947a6843379",
+ "message": "Recipe glusterfs, version 0.0.1 saved.",
+ "revision": null,
+ "timestamp": "2017-11-08T00:02:47Z"
+ }
+ ],
+ "name": "glusterfs",
+ "total": 6
+ }
+ ]
+ }
+
+POST `/api/v0/recipes/new`
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Create a new recipe, or update an existing recipe. This supports both JSON and TOML
+ for the recipe format. The recipe should be in the body of the request with the
+ `Content-Type` header set to either `application/json` or `text/x-toml`.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+DELETE `/api/v0/recipes/delete/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Delete a recipe. The recipe is deleted from the branch, and will no longer
+ be listed by the `list` route. A recipe can be undeleted using the `undo` route
+ to revert to a previous commit.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+POST `/api/v0/recipes/workspace`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Write a recipe to the temporary workspace. This works exactly the same as `new` except
+ that it does not create a commit. JSON and TOML bodies are supported.
+
+ The workspace is meant to be used as a temporary recipe storage for clients.
+ It will be read by the `info` and `diff` routes if it is different from the
+ most recent commit.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+DELETE `/api/v0/recipes/workspace/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Remove the temporary workspace copy of a recipe. The `info` route will now
+ return the most recent commit of the recipe. Any changes that were in the
+ workspace will be lost.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+POST `/api/v0/recipes/undo//`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ This will revert the recipe to a previous commit. The commit hash from the `changes`
+ route can be used in this request.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+POST `/api/v0/recipes/tag/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Tag a recipe as a new release. This uses git tags with a special format.
+ `refs/tags///r`. Only the most recent recipe commit
+ can be tagged. Revisions start at 1 and increment for each new tag
+ (per-recipe). If the commit has already been tagged it will return false.
+
+ The response will be a status response with `status` set to true, or an
+ error response with it set to false and an error message included.
+
+`/api/v0/recipes/diff///`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return the differences between two commits, or the workspace. The commit hash
+ from the `changes` response can be used here, or several special strings:
+
+ - NEWEST will select the newest git commit. This works for `from_commit` or `to_commit`
+ - WORKSPACE will select the workspace copy. This can only be used in `to_commit`
+
+ eg. `/api/v0/recipes/diff/glusterfs/NEWEST/WORKSPACE` will return the differences
+ between the most recent git commit and the contents of the workspace.
+
+ Each entry in the response's diff object contains the old recipe value and the new one.
+ If old is null and new is set, then it was added.
+ If new is null and old is set, then it was removed.
+ If both are set, then it was changed.
+
+ The old/new entries will have the name of the recipe field that was changed. This
+ can be one of: Name, Description, Version, Module, or Package.
+ The contents for these will be the old/new values for them.
+
+ In the example below the version was changed and the ping package was added.
+
+ Example::
+
+ {
+ "diff": [
+ {
+ "new": {
+ "Version": "0.0.6"
+ },
+ "old": {
+ "Version": "0.0.5"
+ }
+ },
+ {
+ "new": {
+ "Package": {
+ "name": "ping",
+ "version": "3.2.1"
+ }
+ },
+ "old": null
+ }
+ ]
+ }
+
+`/api/v0/recipes/freeze/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return a JSON representation of the recipe with the package and module versions set
+ to the exact versions chosen by depsolving the recipe.
+
+ Example::
+
+ {
+ "errors": [],
+ "recipes": [
+ {
+ "recipe": {
+ "description": "An example GlusterFS server with samba",
+ "modules": [
+ {
+ "name": "glusterfs",
+ "version": "3.8.4-18.4.el7.x86_64"
+ },
+ {
+ "name": "glusterfs-cli",
+ "version": "3.8.4-18.4.el7.x86_64"
+ }
+ ],
+ "name": "glusterfs",
+ "packages": [
+ {
+ "name": "ping",
+ "version": "2:3.2.1-2.el7.noarch"
+ },
+ {
+ "name": "samba",
+ "version": "4.6.2-8.el7.x86_64"
+ }
+ ],
+ "version": "0.0.6"
+ }
+ }
+ ]
+ }
+
+`/api/v0/recipes/depsolve/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Depsolve the recipe using yum, return the recipe used, and the NEVRAs of the packages
+ chosen to satisfy the recipe's requirements. The response will include a list of results,
+ with the full dependency list in `dependencies`, the NEVRAs for the recipe's direct modules
+ and packages in `modules`, and any error will be in `errors`.
+
+ Example::
+
+ {
+ "errors": [],
+ "recipes": [
+ {
+ "dependencies": [
+ {
+ "arch": "noarch",
+ "epoch": "0",
+ "name": "2ping",
+ "release": "2.el7",
+ "version": "3.2.1"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "acl",
+ "release": "12.el7",
+ "version": "2.2.51"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "audit-libs",
+ "release": "3.el7",
+ "version": "2.7.6"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "avahi-libs",
+ "release": "17.el7",
+ "version": "0.6.31"
+ },
+ ...
+ ],
+ "modules": [
+ {
+ "arch": "noarch",
+ "epoch": "0",
+ "name": "2ping",
+ "release": "2.el7",
+ "version": "3.2.1"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "glusterfs",
+ "release": "18.4.el7",
+ "version": "3.8.4"
+ },
+ ...
+ ],
+ "recipe": {
+ "description": "An example GlusterFS server with samba",
+ "modules": [
+ {
+ "name": "glusterfs",
+ "version": "3.7.*"
+ },
+ ...
+ }
+ }
+ ]
+ }
+
+`/api/v0/projects/list[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ List all of the available projects. By default this returns the first 20 items,
+ but this can be changed by setting the `offset` and `limit` arguments.
+
+ Example::
+
+ {
+ "limit": 20,
+ "offset": 0,
+ "projects": [
+ {
+ "description": "0 A.D. (pronounced \"zero ey-dee\") is a ...",
+ "homepage": "http://play0ad.com",
+ "name": "0ad",
+ "summary": "Cross-Platform RTS Game of Ancient Warfare",
+ "upstream_vcs": "UPSTREAM_VCS"
+ },
+ ...
+ ],
+ "total": 21770
+ }
+
+`/api/v0/projects/info/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return information about the comma-separated list of projects. It includes the description
+ of the package along with the list of available builds.
+
+ Example::
+
+ {
+ "projects": [
+ {
+ "builds": [
+ {
+ "arch": "x86_64",
+ "build_config_ref": "BUILD_CONFIG_REF",
+ "build_env_ref": "BUILD_ENV_REF",
+ "build_time": "2017-03-01T08:39:23",
+ "changelog": "- restore incremental backups correctly, files ...",
+ "epoch": "2",
+ "metadata": {},
+ "release": "32.el7",
+ "source": {
+ "license": "GPLv3+",
+ "metadata": {},
+ "source_ref": "SOURCE_REF",
+ "version": "1.26"
+ }
+ }
+ ],
+ "description": "The GNU tar program saves many ...",
+ "homepage": "http://www.gnu.org/software/tar/",
+ "name": "tar",
+ "summary": "A GNU file archiving program",
+ "upstream_vcs": "UPSTREAM_VCS"
+ }
+ ]
+ }
+
+`/api/v0/projects/depsolve/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Depsolve the comma-separated list of projects and return the list of NEVRAs needed
+ to satisfy the request.
+
+ Example::
+
+ {
+ "projects": [
+ {
+ "arch": "noarch",
+ "epoch": "0",
+ "name": "basesystem",
+ "release": "7.el7",
+ "version": "10.0"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "bash",
+ "release": "28.el7",
+ "version": "4.2.46"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "filesystem",
+ "release": "21.el7",
+ "version": "3.2"
+ },
+ ...
+ ]
+ }
+
+`/api/v0/modules/list[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return a list of all of the available modules. This includes the name and the
+ group_type, which is always "rpm" for lorax-composer. By default this returns
+ the first 20 items. This can be changed by setting the `offset` and `limit`
+ arguments.
+
+ Example::
+
+ {
+ "limit": 20,
+ "modules": [
+ {
+ "group_type": "rpm",
+ "name": "0ad"
+ },
+ {
+ "group_type": "rpm",
+ "name": "0ad-data"
+ },
+ {
+ "group_type": "rpm",
+ "name": "0install"
+ },
+ {
+ "group_type": "rpm",
+ "name": "2048-cli"
+ },
+ ...
+ ]
+ "total": 21770
+ }
+
+`/api/v0/modules/list/[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return the list of comma-separated modules. Output is the same as `/modules/list`
+
+ Example::
+
+ {
+ "limit": 20,
+ "modules": [
+ {
+ "group_type": "rpm",
+ "name": "tar"
+ }
+ ],
+ "offset": 0,
+ "total": 1
+ }
+
+`/api/v0/modules/info/`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Return the module's dependencies, and the information about the module.
+
+ Example::
+
+ {
+ "modules": [
+ {
+ "dependencies": [
+ {
+ "arch": "noarch",
+ "epoch": "0",
+ "name": "basesystem",
+ "release": "7.el7",
+ "version": "10.0"
+ },
+ {
+ "arch": "x86_64",
+ "epoch": "0",
+ "name": "bash",
+ "release": "28.el7",
+ "version": "4.2.46"
+ },
+ ...
+ ],
+ "description": "The GNU tar program saves ...",
+ "homepage": "http://www.gnu.org/software/tar/",
+ "name": "tar",
+ "summary": "A GNU file archiving program",
+ "upstream_vcs": "UPSTREAM_VCS"
+ }
+ ]
+ }
+"""
+
import logging
log = logging.getLogger("lorax-composer")
@@ -48,10 +647,20 @@ except ImportError:
libvirt = None
def take_limits(iterable, offset, limit):
+ """ Apply offset and limit to an iterable object
+
+ :param iterable: The object to limit
+ :type iterable: iter
+ :param offset: The number of items to skip
+ :type offset: int
+ :param limit: The total number of items to return
+ :type limit: int
+ :returns: A subset of the iterable
+ """
return iterable[offset:][:limit]
def v0_api(api):
- """ Setup v0 of the API server"""
+ # Note that Sphinx will not generate documentations for any of these.
@api.route("/api/v0/test")
@crossdomain(origin="*")
def v0_test():