From c2d2e053427c1b39fe7e0fe61bc0f85b94ba4b56 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Jun 02 2017 22:25:49 +0000 Subject: PR#434 devtools: fakehub and fakeweb Merges #434 https://pagure.io/koji/pull-request/434 --- diff --git a/devtools/README.md b/devtools/README.md new file mode 100644 index 0000000..d829363 --- /dev/null +++ b/devtools/README.md @@ -0,0 +1,44 @@ +Koji Developer Tools +-------------------- + +This directory contains some tools that developers may find useful. + +fakehub +------- + +This script runs a single hub call in the foreground (no httpd) and +dumps the results. It runs using the code from the checkout. + +The call to be executed is specified on the command line, much like +the ``koji call`` command. + +For example: +``` +[mike@localhost koji]$ devtools/fakehub getTag 1 +``` + +You will see hub logs on the console. The call result is pretty printed +at the end. + +This tool makes it possible to run hub code through the debugger or +or profiler with relative ease. + +fakehub looks for ``fakehub.conf`` or ``fakehub.conf.d`` in the devtools +directory. If either is present, then +``koji.hub.ConfigFile`` and ``koji.hub.ConfigDir`` are set to these values. +If neither is, then the code will fall back to the default (system) config. + + +fakeweb +------- + +This tool is similar to the fakehub tool, but instead of a single pass it starts +a web server on port 8000 and starts serving. + +As with the fakehub tool, fakeweb runs in the foreground in a single thread, making +it possible to debug web code. + +Similar to fakehub, fakeweb looks for ``fakeweb.conf`` or ``fakeweb.conf.d`` +in the devtools directory. If either is present, then +``koji.web.ConfigFile`` and ``koji.web.ConfigDir`` are set to these values. +If neither is, then the code will fall back to the default (system) config. diff --git a/devtools/fakehub b/devtools/fakehub new file mode 100755 index 0000000..fed749d --- /dev/null +++ b/devtools/fakehub @@ -0,0 +1,107 @@ +#!/usr/bin/python + +from __future__ import absolute_import +from __future__ import print_function +import ast +import os +import os.path +import pprint +import sys +import six.moves.xmlrpc_client +from six.moves import cStringIO +from six.moves.urllib.parse import quote + +sys.path.insert(0, os.getcwd()) +sys.path.insert(1, os.path.join(os.getcwd(), 'hub')) +import koji +import kojixmlrpc + + +def get_url(environ): + url = environ['wsgi.url_scheme']+'://' + + if environ.get('HTTP_HOST'): + url += environ['HTTP_HOST'] + else: + url += environ['SERVER_NAME'] + + if environ['wsgi.url_scheme'] == 'https': + if environ['SERVER_PORT'] != '443': + url += ':' + environ['SERVER_PORT'] + else: + if environ['SERVER_PORT'] != '80': + url += ':' + environ['SERVER_PORT'] + + url += quote(environ.get('SCRIPT_NAME', '')) + url += quote(environ.get('PATH_INFO', '')) + if environ.get('QUERY_STRING'): + url += '?' + environ['QUERY_STRING'] + return url + + +def nice_literal(value): + try: + return ast.literal_eval(value) + except (ValueError, SyntaxError): + return value + + +def get_request(): + method = sys.argv[1] + args = [] + kwargs = {} + for s in sys.argv[2:]: + if '=' in s: + k, v = s.split('=', 1) + v = nice_literal(v) + kwargs[k] = v + else: + args.append(nice_literal(s)) + args = koji.encode_args(*args, **kwargs) + request = six.moves.xmlrpc_client.dumps(args, method, allow_none=1) + return request + + +def start_response(status, headers): + pprint.pprint("Status: %r" % status) + pprint.pprint("Headers: %r" % headers) + + +def parse_response(data): + p, u = six.moves.xmlrpc_client.getparser() + for chunk in data: + p.feed(chunk) + p.close() + result = u.close() + if len(result) == 1: + result = result[0] + return result + + +def set_config(environ): + lconfig = "%s/devtools/fakehub.conf" % os.getcwd() + lconfigd = "%s/devtools/fakehub.conf.d" % os.getcwd() + if os.path.exists(lconfig) or os.path.exists(lconfigd): + environ['koji.hub.ConfigFile'] = lconfig + environ['koji.hub.ConfigDir'] = lconfigd + + +def main(): + environ = {} + environ['SCRIPT_FILENAME'] = kojixmlrpc.__file__ + environ['wsgi.url_scheme'] = 'https' + environ['SERVER_NAME'] = 'myserver' + environ['SERVER_PORT'] = '443' + environ['REQUEST_URI'] = get_url(environ) + environ['wsgi.input'] = cStringIO(get_request()) + environ['REQUEST_METHOD'] = 'POST' + environ['CONTENT_TYPE'] = 'text/xml' + set_config(environ) + print('RESULT:') + data = kojixmlrpc.application(environ, start_response) + result = parse_response(data) + pprint.pprint(result) + + +if __name__ == '__main__': + main() diff --git a/devtools/fakehub.conf.sample b/devtools/fakehub.conf.sample new file mode 100644 index 0000000..0b73706 --- /dev/null +++ b/devtools/fakehub.conf.sample @@ -0,0 +1,19 @@ +[hub] + +# example configuration for fakehub +DBName = koji +DBUser = koji +KojiDir = /mnt/koji +DisableNotifications = True + +EnableMaven = True +EnableWin = True + +CheckClientIP = False + +#KojiDebug = On +LogLevel = DEBUG + +#KojiTraceback = normal +KojiTraceback = extended + diff --git a/devtools/fakeweb b/devtools/fakeweb new file mode 100755 index 0000000..735a97b --- /dev/null +++ b/devtools/fakeweb @@ -0,0 +1,131 @@ +#!/usr/bin/python + +from __future__ import absolute_import +from __future__ import print_function +import mimetypes +import os +import os.path +import pprint +import sys + +from six.moves.urllib.parse import quote +from wsgiref.util import setup_testing_defaults +from wsgiref.simple_server import make_server + +CWD = os.getcwd() +sys.path.insert(0, CWD) +sys.path.insert(1, os.path.join(CWD, 'www/lib')) +sys.path.insert(1, os.path.join(CWD, 'www/kojiweb')) +import wsgi_publisher + + +def get_url(environ): + url = environ['wsgi.url_scheme']+'://' + + if environ.get('HTTP_HOST'): + url += environ['HTTP_HOST'] + else: + url += environ['SERVER_NAME'] + + if environ['wsgi.url_scheme'] == 'https': + if environ['SERVER_PORT'] != '443': + url += ':' + environ['SERVER_PORT'] + else: + if environ['SERVER_PORT'] != '80': + url += ':' + environ['SERVER_PORT'] + + url += quote(environ.get('SCRIPT_NAME', '')) + url += quote(environ.get('PATH_INFO', '')) + if environ.get('QUERY_STRING'): + url += '?' + environ['QUERY_STRING'] + return url + +FIRST = True + + +def do_static(environ, start_response): + redirect = os.environ.get('STATIC_URL', '') + if redirect: + environ['STATIC_URL'] = redirect + return redirect_static(environ, start_response) + # otherwise serve our local static files + path = environ.get('PATH_INFO', '') + assert path.startswith('/koji-static') + path = path[12:] + path = path.lstrip('/') + fn = os.path.join(CWD, 'www/static', path) + if not os.path.exists(fn): + print("No such file: %s" % fn) + return do_404(environ, start_response) + size = os.path.getsize(fn) + ctype, encoding = mimetypes.guess_type(fn) + headers = [ + ('Content-Length', str(size)), + ('Content-Type', ctype), + ] + start_response('200 OK', headers) + return iter_file(fn) + + +def do_404(environ, start_response): + content = 'URL not found\n' + headers = [ + ('Content-Length', str(len(content))), + ('Content-Type', 'text/plain'), + ] + start_response('404 Not Found', headers) + return [content] + + +def iter_file(fn): + with open(fn, 'rb') as fo: + while True: + chunk = fo.read(8192) + if not chunk: + break + yield chunk + + +def redirect_static(environ, start_response): + response = '' + headers = [ + ('Content-Length', str(len(response))), + ('Content-Type', "text/plain"), + ('Location', environ['STATIC_URL'] + environ['PATH_INFO']), + ] + start_response('302 Found', headers) + return [response] + + +def set_config(environ): + lconfig = "%s/devtools/fakeweb.conf" % os.getcwd() + lconfigd = "%s/devtools/fakeweb.conf.d" % os.getcwd() + if os.path.exists(lconfig) or os.path.exists(lconfigd): + environ['koji.web.ConfigFile'] = lconfig + environ['koji.web.ConfigDir'] = lconfigd + + +def application(environ, start_response): + global FIRST + setup_testing_defaults(environ) + # provide some needed info + environ['SCRIPT_FILENAME'] = wsgi_publisher.__file__ + environ['REQUEST_URI'] = get_url(environ) + set_config(environ) + if FIRST: + pprint.pprint(environ) + FIRST = False + path = environ.get('PATH_INFO', '') + if path.startswith('/koji-static'): + return do_static(environ, start_response) + return wsgi_publisher.application(environ, start_response) + + +def main(): + # koji.add_file_logger('koji', 'fakeweb.log') + httpd = make_server('', 8000, application) + print("Serving on port 8000...") + httpd.serve_forever() + +if __name__ == '__main__': + main() diff --git a/devtools/fakeweb.conf.sample b/devtools/fakeweb.conf.sample new file mode 100644 index 0000000..1145a3e --- /dev/null +++ b/devtools/fakeweb.conf.sample @@ -0,0 +1,14 @@ +[web] +SiteName = koji + +# Key urls +KojiHubURL = http://localhost/kojihub +KojiFilesURL = http://localhost/kojifiles + +Secret = toomanysecrets + +PythonDebug = on +LogLevel = DEBUG + +# prevent web code from tweaking the path +LibPath = /nosuchpath