
The ssl_port was also renamed to proxy_port for a better meaning. And then in future, other proxy than nginx can be supported. This patch also updates tests cases and remove unused functions (or plugin-specific function) from tests/utils.py Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API/config.md | 4 +-- docs/wokd.8.in | 16 +++------ src/wok.conf.in | 23 +------------ src/wok/config.py.in | 7 +--- src/wok/model/config.py | 2 +- src/wokd.in | 18 ++-------- tests/test_api.py | 14 +++----- tests/test_exception.py | 52 +++++++---------------------- tests/test_plugin.py | 12 ++----- tests/test_server.py | 15 ++------- tests/test_server_root.py | 17 +++------- tests/utils.py | 83 ++++++++--------------------------------------- 12 files changed, 53 insertions(+), 210 deletions(-) diff --git a/docs/API/config.md b/docs/API/config.md index bdf5c16..0c273e2 100644 --- a/docs/API/config.md +++ b/docs/API/config.md @@ -9,7 +9,7 @@ Contains information about the application environment and configuration. **Methods:** * **GET**: Retrieve configuration information - * ssl_port: SSL port to list on + * proxy_port: SSL port to list on * websockets_port: Port for websocket proxy to listen on * auth: Authentication method used to log in to Wok * version: Wok version @@ -22,7 +22,7 @@ Contains information about the application environment and configuration. #### Examples GET /config { - ssl_port: 8001, + proxy_port: 8001, websockets_port: 64667, version: 2.0 } diff --git a/docs/wokd.8.in b/docs/wokd.8.in index c7a6f3f..4dc0730 100644 --- a/docs/wokd.8.in +++ b/docs/wokd.8.in @@ -3,10 +3,10 @@ Kimchi \- HTML5 based management tool for KVM .SH SYNOPSIS .B kimchid -[\fB-h\fP|\fB--help\fP] [\fB--host\fP \fIhost\fP] [\fB--port\fP \fIport\fP] -[\fB--ssl-port\fP \fIssl_port\fP] [\fB--cherrypy_port\fP \fIcherrypy_port\fP] -[\fB--log-level\fP \fIlog_level\fP] [\fB--access-log\fP \fIaccess_log\fP] -[\fB--error-log\fP \fIerror_log\fP] [\fB--environment\fP \fIenvironment\fP] +[\fB-h\fP|\fB--help\fP] [\fB--proxy-port\fP \fIproxy_port\fP] +[\fB--cherrypy_port\fP \fIcherrypy_port\fP] [\fB--log-level\fP \fIlog_level\fP] +[\fB--access-log\fP \fIaccess_log\fP] [\fB--error-log\fP \fIerror_log\fP] +[\fB--environment\fP \fIenvironment\fP] .SH DESCRIPTION \fBKimchi\fP is an HTML5 based management tool for KVM. It is designed to make it as easy as possible to get started with KVM and create your first guest. @@ -19,13 +19,7 @@ The following options are supported: \fB\-h\fP , \fB\-\-help\fP Show this help message and exit. .TP -\fB\-\-host\fP \fIhost\fP -Specify the hostname or IP to listen on. -.TP -\fB\-\-port\fP \fIport\fP -Specify the HTTP port (default \fI8000\fP). -.TP -\fB\-\-ssl-port\fP \fIssl_port\fP +\fB\-\-proxy-port\fP \fIproxy_port\fP Specify the HTTPS port (default \fI8001\fP). .TP \fB\-\-cherrypy_port\fP \fIcherrypy_port\fP diff --git a/src/wok.conf.in b/src/wok.conf.in index 254f786..3806609 100644 --- a/src/wok.conf.in +++ b/src/wok.conf.in @@ -3,18 +3,8 @@ # [server] -# Hostname or IP address to listen on -#host = 0.0.0.0 - -# Port to listen on -#port = 8000 - # Start an SSL-enabled server on the given port -#ssl_port = 8001 - -# Allow user disables HTTP port. In that case, all the connections -# will be done directly through HTTPS port (values: true|false) -#https_only = false +#proxy_port = 8001 # Cherrypy server port #cherrypy_port = 8010 @@ -26,17 +16,6 @@ # terminates it automatically. #session_timeout = 10 -# The full path to an SSL Certificate or chain of certificates in -# PEM format. When a chain is used, the server's certificate must be -# the first certificate in the file with the chain concatenated into -# the end of that certificate. If left unspecified, Wok will generate -# a self-signed certificate automatically. -#ssl_cert = - -# The corresponding private key in PEM format for the SSL Certificate supplied -# above. If left blank, Wok will generate a self-signed certificate. -#ssl_key = - # Running environment of the server #environment = production diff --git a/src/wok/config.py.in b/src/wok/config.py.in index c48c46a..9573e66 100644 --- a/src/wok/config.py.in +++ b/src/wok/config.py.in @@ -262,15 +262,10 @@ class PluginConfig(dict): def _get_config(): config = SafeConfigParser() config.add_section("server") - config.set("server", "host", "0.0.0.0") - config.set("server", "port", "8000") - config.set("server", "ssl_port", "8001") - config.set("server", "https_only", "false") + config.set("server", "proxy_port", "8001") config.set("server", "cherrypy_port", "8010") config.set("server", "websockets_port", "64667") config.set("server", "session_timeout", "10") - config.set("server", "ssl_cert", "") - config.set("server", "ssl_key", "") config.set("server", "environment", "production") config.set('server', 'max_body_size', '4*1024*1024') config.set("server", "server_root", "") diff --git a/src/wok/model/config.py b/src/wok/model/config.py index 3748a6e..7e8ae4f 100644 --- a/src/wok/model/config.py +++ b/src/wok/model/config.py @@ -25,7 +25,7 @@ class ConfigModel(object): pass def lookup(self, name): - return {'ssl_port': config.get('server', 'ssl_port'), + return {'proxy_port': config.get('server', 'proxy_port'), 'websockets_port': config.get('server', 'websockets_port'), 'auth': config.get('authentication', 'method'), 'server_root': config.get('server', 'server_root'), diff --git a/src/wokd.in b/src/wokd.in index c1b302c..339e4e1 100644 --- a/src/wokd.in +++ b/src/wokd.in @@ -43,10 +43,7 @@ def main(options): if not os.geteuid() == 0: sys.exit("\nMust be root to run this script. Exiting ...\n") - host = config.config.get("server", "host") - port = config.config.get("server", "port") - ssl_port = config.config.get("server", "ssl_port") - https_only = config.config.get("server", "https_only") + proxy_port = config.config.get("server", "proxy_port") cherrypy_port = config.config.get("server", "cherrypy_port") websockets_port = config.config.get("server", "websockets_port") session_timeout = config.config.get("server", "session_timeout") @@ -56,15 +53,8 @@ def main(options): logLevel = config.config.get("logging", "log_level") parser = OptionParser() - parser.add_option('--host', type="string", default=host, - help="Hostname to listen on") - parser.add_option('--port', type="int", default=port, - help="Port to listen on (default %s)" % port) - parser.add_option('--ssl-port', type="int", default=ssl_port, - help="Port to enable SSL (default %s)" % ssl_port) - parser.add_option('--https_only', type="choice", default=https_only, - choices=['false', 'true'], - help="Disable HTTP port (default %s)" % ssl_port) + parser.add_option('--proxy-port', type="int", default=proxy_port, + help="Port to enable SSL (default %s)" % proxy_port) parser.add_option('--cherrypy_port', type="int", default=cherrypy_port, help="Cherrypy server port (default %s)" % cherrypy_port) parser.add_option('--websockets_port', type="int", default=websockets_port, @@ -98,8 +88,6 @@ def main(options): config.config.set(sec, item, str(getattr(options, item))) # Add non-option arguments - setattr(options, 'ssl_cert', config.config.get('server', 'ssl_cert')) - setattr(options, 'ssl_key', config.config.get('server', 'ssl_key')) setattr(options, 'max_body_size', config.config.get('server', 'max_body_size')) diff --git a/tests/test_api.py b/tests/test_api.py index c93aff0..23c263d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -28,19 +28,13 @@ from wok.asynctask import AsyncTask test_server = None model = None -host = None -port = None -ssl_port = None def setUpModule(): - global test_server, model, host, port, ssl_port + global test_server, model utils.patch_auth() - host = '127.0.0.1' - port = utils.get_free_port('http') - ssl_port = utils.get_free_port('https') - test_server = utils.run_server(host, port, ssl_port, test_mode=True) + test_server = utils.run_server(test_mode=True) def tearDownModule(): @@ -50,12 +44,12 @@ def tearDownModule(): class APITests(unittest.TestCase): def setUp(self): - self.request = partial(utils.request, host, ssl_port) + self.request = partial(utils.request) def test_config(self): resp = self.request('/config').read() conf = json.loads(resp) - keys = ["auth", "ssl_port", "websockets_port", "version", + keys = ["auth", "proxy_port", "websockets_port", "version", "server_root"] self.assertEquals(sorted(keys), sorted(conf.keys())) diff --git a/tests/test_exception.py b/tests/test_exception.py index 31eca08..8dd20d8 100644 --- a/tests/test_exception.py +++ b/tests/test_exception.py @@ -22,25 +22,18 @@ import json import unittest -from utils import get_free_port, patch_auth, request, run_server +from utils import patch_auth, request, run_server test_server = None model = None -host = None -port = None -ssl_port = None def setup_server(environment='development'): - global test_server, model, host, port, ssl_port + global test_server, model patch_auth() - host = '127.0.0.1' - port = get_free_port('http') - ssl_port = get_free_port('https') - test_server = run_server(host, port, ssl_port, test_mode=True, - environment=environment) + test_server = run_server(test_mode=True, environment=environment) class ExceptionTests(unittest.TestCase): @@ -54,37 +47,26 @@ class ExceptionTests(unittest.TestCase): setup_server('production') # test 404 - resp = json.loads(request(host, ssl_port, '/tasks/blah').read()) + resp = json.loads(request('/tasks/blah').read()) self.assertEquals('404 Not Found', resp.get('code')) # test 405 wrong method - resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read()) + resp = json.loads(request('/', None, 'DELETE').read()) msg = u'WOKAPI0002E: Delete is not allowed for wokroot' self.assertEquals('405 Method Not Allowed', resp.get('code')) self.assertEquals(msg, resp.get('reason')) # test 400 parse error - resp = json.loads(request(host, ssl_port, '/tasks', '{', + resp = json.loads(request('/tasks', '{', 'POST').read()) msg = u'WOKAPI0006E: Unable to parse JSON request' self.assertEquals('400 Bad Request', resp.get('code')) self.assertEquals(msg, resp.get('reason')) self.assertNotIn('call_stack', resp) - # test 400 missing required parameter - # TODO: need add this test when some REST API from wok accepts POST -# req = json.dumps({}) -# resp = json.loads(request(host, ssl_port, '/tasks', req, -# 'POST').read()) -# self.assertEquals('400 Bad Request', resp.get('code')) -# m = u"KCHVM0016E: Specify a template to create a virtual machine from" -# self.assertEquals(m, resp.get('reason')) -# self.assertNotIn('call_stack', resp) - # test 405 method not allowed req = json.dumps({}) - resp = json.loads(request(host, ssl_port, '/tasks', req, - 'POST').read()) + resp = json.loads(request('/tasks', req, 'POST').read()) m = u"WOKAPI0005E: Create is not allowed for tasks" self.assertEquals('405 Method Not Allowed', resp.get('code')) self.assertEquals(m, resp.get('reason')) @@ -95,37 +77,25 @@ class ExceptionTests(unittest.TestCase): """ setup_server() # test 404 - resp = json.loads(request(host, ssl_port, '/tasks/blah').read()) + resp = json.loads(request('/tasks/blah').read()) self.assertEquals('404 Not Found', resp.get('code')) # test 405 wrong method - resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read()) + resp = json.loads(request('/', None, 'DELETE').read()) msg = u'WOKAPI0002E: Delete is not allowed for wokroot' self.assertEquals('405 Method Not Allowed', resp.get('code')) self.assertEquals(msg, resp.get('reason')) # test 400 parse error - resp = json.loads(request(host, ssl_port, '/tasks', '{', - 'POST').read()) + resp = json.loads(request('/tasks', '{', 'POST').read()) msg = u'WOKAPI0006E: Unable to parse JSON request' self.assertEquals('400 Bad Request', resp.get('code')) self.assertEquals(msg, resp.get('reason')) self.assertIn('call_stack', resp) - # test 400 missing required parameter - # TODO: need add this test when some REST API from wok accepts POST -# req = json.dumps({}) -# resp = json.loads(request(host, ssl_port, '/tasks', req, -# 'POST').read()) -# m = u"KCHVM0016E: Specify a template to create a virtual machine from" -# self.assertEquals('400 Bad Request', resp.get('code')) -# self.assertEquals(m, resp.get('reason')) -# self.assertIn('call_stack', resp) - # test 405 method not allowed req = json.dumps({}) - resp = json.loads(request(host, ssl_port, '/tasks', req, - 'POST').read()) + resp = json.loads(request('/tasks', req, 'POST').read()) m = u"WOKAPI0005E: Create is not allowed for tasks" self.assertEquals('405 Method Not Allowed', resp.get('code')) self.assertEquals(m, resp.get('reason')) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 7c5e8e8..312412c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -30,19 +30,13 @@ import utils test_server = None model = None -host = None -port = None -ssl_port = None def setUpModule(): - global test_server, model, host, port, ssl_port + global test_server, model utils.patch_auth() - host = '127.0.0.1' - port = utils.get_free_port('http') - ssl_port = utils.get_free_port('https') - test_server = utils.run_server(host, port, ssl_port, test_mode=True) + test_server = utils.run_server(test_mode=True) def tearDownModule(): @@ -55,7 +49,7 @@ def tearDownModule(): class PluginTests(unittest.TestCase): def setUp(self): - self.request = partial(utils.request, host, ssl_port) + self.request = partial(utils.request) def _create_rectangle(self, name, length, width): req = json.dumps({'name': name, 'length': length, 'width': width}) diff --git a/tests/test_server.py b/tests/test_server.py index 1f5a236..f1950e0 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -34,24 +34,15 @@ import utils test_server = None model = None -host = None -port = None -ssl_port = None -cherrypy_port = None tmpfile = None def setUpModule(): - global test_server, model, host, port, ssl_port, cherrypy_port, tmpfile + global test_server, model, tmpfile utils.patch_auth() tmpfile = tempfile.mktemp() - host = '127.0.0.1' - port = utils.get_free_port('http') - ssl_port = utils.get_free_port('https') - cherrypy_port = utils.get_free_port('cherrypy_port') - test_server = utils.run_server(host, port, ssl_port, test_mode=True, - cherrypy_port=cherrypy_port) + test_server = utils.run_server(test_mode=True) def tearDownModule(): @@ -60,7 +51,7 @@ def tearDownModule(): class ServerTests(unittest.TestCase): def setUp(self): - self.request = partial(utils.request, host, ssl_port) + self.request = partial(utils.request) def assertValidJSON(self, txt): try: diff --git a/tests/test_server_root.py b/tests/test_server_root.py index fb5cf6c..e95a13b 100644 --- a/tests/test_server_root.py +++ b/tests/test_server_root.py @@ -20,25 +20,18 @@ import json import unittest -from utils import get_free_port, patch_auth, request, run_server +from utils import patch_auth, request, run_server test_server = None model = None -host = None -port = None -ssl_port = None def setup_server(environment='development', server_root=''): - global test_server, model, host, port, ssl_port + global test_server, model patch_auth() - host = '127.0.0.1' - port = get_free_port('http') - ssl_port = get_free_port('https') - test_server = run_server(host, port, ssl_port, test_mode=True, - environment=environment, + test_server = run_server(test_mode=True, environment=environment, server_root=server_root) @@ -54,7 +47,7 @@ class ServerRootTests(unittest.TestCase): setup_server('production', server_root) # check if server_root in config is the same used to start server - resp = request(host, ssl_port, server_root + '/config').read() + resp = request(server_root + '/config').read() conf = json.loads(resp) self.assertEquals(len(conf), 5) @@ -66,6 +59,6 @@ class ServerRootTests(unittest.TestCase): setup_server(server_root=server_root) # check if server_root in config is the same used to start server - resp = request(host, ssl_port, server_root + '/config').read() + resp = request(server_root + '/config').read() conf = json.loads(resp) self.assertEquals(len(conf), 5) diff --git a/tests/utils.py b/tests/utils.py index d518f13..0c10ccb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -25,25 +25,23 @@ import cherrypy import grp import httplib import inspect -import json import os -import socket import ssl import sys import threading import time import unittest -from contextlib import closing -from lxml import etree import wok.server -from wok.config import config, PluginPaths + from wok.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs +from wok.config import config from wok.exception import NotFoundError, OperationFailed from wok.utils import wok_log +HOST = '0.0.0.0' +PROXY_PORT = 8001 -_ports = {} fake_user = {'root': 'letmein!'} @@ -90,37 +88,15 @@ if sys.version_info[:2] == (2, 6): unittest.TestCase.assertNotIn = assertNotIn -def get_free_port(name='http'): - global _ports - if _ports.get(name) is not None: - return _ports[name] - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - with closing(sock): - try: - sock.bind(("0.0.0.0", 0)) - except: - raise Exception("Could not find a free port") - _ports[name] = sock.getsockname()[1] - return _ports[name] - - -def run_server(host, port, ssl_port, test_mode, cherrypy_port=None, - model=None, environment='development', server_root=''): - - if cherrypy_port is None: - cherrypy_port = get_free_port('cherrypy_port') - - if ssl_port is None: - ssl_port = get_free_port('https') +def run_server(test_mode, model=None, environment='dev', server_root=''): args = type('_', (object,), - {'host': host, 'port': port, 'ssl_port': ssl_port, - 'https_only': 'false', 'cherrypy_port': cherrypy_port, - 'websockets_port': 64667, 'ssl_cert': '', 'ssl_key': '', - 'max_body_size': '4*1024', 'test': test_mode, - 'access_log': '/dev/null', 'error_log': '/dev/null', - 'environment': environment, 'log_level': 'debug', - 'session_timeout': 10, 'server_root': server_root})() + {'cherrypy_port': 8010, 'max_body_size': '4*1024', + 'test': test_mode, 'access_log': '/dev/null', + 'error_log': '/dev/null', 'environment': environment, + 'log_level': 'debug', 'session_timeout': 10, + 'server_root': server_root})() + if model is not None: setattr(args, 'model', model) @@ -132,13 +108,6 @@ def run_server(host, port, ssl_port, test_mode, cherrypy_port=None, return s -def silence_server(): - """ - Silence server status messages on stdout - """ - cherrypy.config.update({"environment": "embedded"}) - - def running_as_root(): return os.geteuid() == 0 @@ -155,36 +124,17 @@ def _request(conn, path, data, method, headers): return conn.getresponse() -def request(host, port, path, data=None, method='GET', headers=None): +def request(path, data=None, method='GET', headers=None): # verify if HTTPSConnection has context parameter if "context" in inspect.getargspec(httplib.HTTPSConnection.__init__).args: context = ssl._create_unverified_context() - conn = httplib.HTTPSConnection(host, port, context=context) + conn = httplib.HTTPSConnection(HOST, PROXY_PORT, context=context) else: - conn = httplib.HTTPSConnection(host, port) + conn = httplib.HTTPSConnection(HOST, PROXY_PORT) return _request(conn, path, data, method, headers) -def get_remote_iso_path(): - """ - Get a remote iso with the right arch from the distro files shipped - with kimchi. - """ - host_arch = os.uname()[4] - remote_path = '' - with open(os.path.join(PluginPaths('kimchi').conf_dir, 'distros.d', - 'fedora.json')) as fedora_isos: - # Get a list of dicts - json_isos_list = json.load(fedora_isos) - for iso in json_isos_list: - if (iso.get('os_arch')) == host_arch: - remote_path = iso.get('path') - break - - return remote_path - - class FakeUser(User): auth_type = "fake" sudo = True @@ -224,11 +174,6 @@ def patch_auth(sudo=True): FakeUser.sudo = sudo -def normalize_xml(xml_str): - return etree.tostring(etree.fromstring(xml_str, - etree.XMLParser(remove_blank_text=True))) - - def wait_task(task_lookup, taskid, timeout=10): for i in range(0, timeout): task_info = task_lookup(taskid) -- 2.7.4