Add /recipes/list route and tests

Includes adding a lock for access to the git repo from the API.
This commit is contained in:
Brian C. Lane 2017-11-07 17:09:56 -08:00
parent 90a8798f4c
commit b6fb22133c
4 changed files with 54 additions and 6 deletions

View File

@ -14,15 +14,17 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections import namedtuple
from flask import Flask from flask import Flask
from pylorax.api.crossdomain import crossdomain from pylorax.api.crossdomain import crossdomain
from pylorax.api.v0 import v0_api from pylorax.api.v0 import v0_api
GitLock = namedtuple("GitLock", ["repo", "lock", "dir"])
server = Flask(__name__) server = Flask(__name__)
__all__ = ["server"] __all__ = ["server", "GitLock"]
@server.route('/') @server.route('/')
@crossdomain(origin="*") @crossdomain(origin="*")

View File

@ -14,13 +14,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from flask import jsonify from flask import jsonify, request
# Use pykickstart to calculate disk image size # Use pykickstart to calculate disk image size
from pykickstart.parser import KickstartParser from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, RHEL7 from pykickstart.version import makeVersion, RHEL7
from pylorax.api.crossdomain import crossdomain from pylorax.api.crossdomain import crossdomain
from pylorax.api.recipes import list_branch_files
from pylorax.creator import DRACUT_DEFAULT, mount_boot_part_over_root from pylorax.creator import DRACUT_DEFAULT, mount_boot_part_over_root
from pylorax.creator import make_appliance, make_image, make_livecd, make_live_images from pylorax.creator import make_appliance, make_image, make_livecd, make_live_images
from pylorax.creator import make_runtime, make_squashfs from pylorax.creator import make_runtime, make_squashfs
@ -29,6 +30,8 @@ from pylorax.imgutils import Mount, PartitionMount, umount
from pylorax.installer import InstallError from pylorax.installer import InstallError
from pylorax.sysutils import joinpaths from pylorax.sysutils import joinpaths
# The API functions don't actually get called by any code here
# pylint: disable=unused-variable
# no-virt mode doesn't need libvirt, so make it optional # no-virt mode doesn't need libvirt, so make it optional
try: try:
@ -42,3 +45,18 @@ def v0_api(api):
@crossdomain(origin="*") @crossdomain(origin="*")
def v0_status(): def v0_status():
return jsonify(build="devel", api="0", db_version="0", schema_version="0", db_supported=False) return jsonify(build="devel", api="0", db_version="0", schema_version="0", db_supported=False)
@api.route("/api/v0/recipes/list")
@crossdomain(origin="*")
def v0_recipes_list():
"""List the available recipes on a branch."""
try:
limit = int(request.args.get("limit", "20"))
offset = int(request.args.get("offset", "0"))
except ValueError:
# TODO return an error
pass
with api.config["GITLOCK"].lock:
recipes = map(lambda f: f[:-5], list_branch_files(api.config["GITLOCK"].repo, "master"))
return jsonify(recipes=recipes, limit=limit, offset=offset, total=len(recipes))

View File

@ -25,10 +25,12 @@ pylorax_log = logging.getLogger("pylorax")
import argparse import argparse
import os import os
import sys import sys
from threading import Lock
from gevent.wsgi import WSGIServer from gevent.wsgi import WSGIServer
from pylorax import vernum from pylorax import vernum
from pylorax.api.server import server from pylorax.api.recipes import open_or_create_repo, commit_recipe_directory
from pylorax.api.server import server, GitLock
VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum) VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum)
@ -105,6 +107,14 @@ if __name__ == '__main__':
sys.exit(1) sys.exit(1)
setup_logging(opts.logfile) setup_logging(opts.logfile)
server.config["REPO_DIR"] = opts.RECIPES
repo = open_or_create_repo(server.config["REPO_DIR"])
server.config["GITLOCK"] = GitLock(repo=repo, lock=Lock(), dir=opts.RECIPES)
# Import example recipes
commit_recipe_directory(server.config["GITLOCK"].repo, "master", opts.RECIPES)
http_server = WSGIServer((opts.host, opts.port), server) http_server = WSGIServer((opts.host, opts.port), server)
# The server writes directly to a file object, so point to our log directory # The server writes directly to a file object, so point to our log directory
server_logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/server.log" server_logfile = os.path.abspath(os.path.dirname(opts.logfile))+"/server.log"

View File

@ -14,20 +14,30 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import os import tempfile
from threading import Lock
import unittest import unittest
from flask import json from flask import json
from pylorax.api.server import server from pylorax.api.recipes import open_or_create_repo, commit_recipe_directory
from pylorax.api.server import server, GitLock
class ServerTestCase(unittest.TestCase): class ServerTestCase(unittest.TestCase):
@classmethod @classmethod
def setUpClass(self): def setUpClass(self):
repo_dir = tempfile.mkdtemp(prefix="lorax.test.repo.")
server.config["REPO_DIR"] = repo_dir
repo = open_or_create_repo(server.config["REPO_DIR"])
server.config["GITLOCK"] = GitLock(repo=repo, lock=Lock(), dir=repo_dir)
server.config['TESTING'] = True server.config['TESTING'] = True
self.server = server.test_client() self.server = server.test_client()
# Import the example recipes
commit_recipe_directory(server.config["GITLOCK"].repo, "master", "tests/pylorax/recipes/")
@classmethod @classmethod
def tearDownClass(self): def tearDownClass(self):
pass pass
@ -38,3 +48,11 @@ class ServerTestCase(unittest.TestCase):
resp = self.server.get("/api/v0/status") resp = self.server.get("/api/v0/status")
data = json.loads(resp.data) data = json.loads(resp.data)
self.assertEqual(data, status_dict) self.assertEqual(data, status_dict)
def test_recipes_list(self):
"""Test the /api/v0/recipes/list route"""
list_dict = {"recipes":["atlas", "development", "glusterfs", "http-server", "jboss", "kubernetes"],
"limit":20, "offset":0, "total":6}
resp = self.server.get("/api/v0/recipes/list")
data = json.loads(resp.data)
self.assertEqual(data, list_dict)