From fca4035996c93f8f05ea3837133961e28a1248d6 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 16 Jun 2014 16:26:31 -0400 Subject: Change test executables into modules Create a common tests framework and convert tests into modules loaded at runtime using the ipsilon plugin framework. Signed-off-by: Simo Sorce --- Makefile | 13 ++- tests/helpers/common.py | 138 +++++++++++++++++++++++++++++++ tests/test1.cfg | 37 --------- tests/test1.py | 90 +++++++++++++++++++- tests/tests.py | 216 ++++++++---------------------------------------- 5 files changed, 270 insertions(+), 224 deletions(-) create mode 100755 tests/helpers/common.py delete mode 100644 tests/test1.cfg diff --git a/Makefile b/Makefile index 0d7e01a..fc8795a 100644 --- a/Makefile +++ b/Makefile @@ -39,9 +39,18 @@ lp-test: ./tests pep8 tests -test: lp-test +wrappers: + #rm -fr wrapdir + #mkdir wrapdir + #LD_PRELOAD=libsocket_wrapper.so + #SOCKET_WRAPPER_DIR=wrapdir + #SOCKET_WRAPPER_DEFAULT_IFACE=9 + +tests: wrappers + PYTHONPATH=./ ./tests/tests.py --test=test1 + +test: lp-test tests PYTHONPATH=./ ./ipsilon/tools/saml2metadata.py - ./tests/tests.py --test=test1 sdist: python setup.py sdist diff --git a/tests/helpers/common.py b/tests/helpers/common.py new file mode 100755 index 0000000..d020a3c --- /dev/null +++ b/tests/helpers/common.py @@ -0,0 +1,138 @@ +#!/usr/bin/python +# +# Copyright (C) 2014 Simo Sorce +# +# see file 'COPYING' for use and warranty information +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import ConfigParser +import io +from ipsilon.util.plugin import PluginObject +import os +import pwd +import shutil +import signal +from string import Template +import subprocess + + +class IpsilonTestBase(PluginObject): + + def __init__(self, name, execname): + super(IpsilonTestBase, self).__init__() + self.name = name + self.execname = execname + self.rootdir = os.getcwd() + self.testdir = None + self.testuser = pwd.getpwuid(os.getuid())[0] + self.processes = [] + + def force_remove(self, op, name, info): + os.chmod(name, 0700) + os.remove(name) + + def setup_base(self, path, test): + self.testdir = os.path.join(path, test.name) + if os.path.exists(self.testdir): + shutil.rmtree(self.testdir, onerror=self.force_remove) + os.makedirs(self.testdir) + shutil.copytree(os.path.join(self.rootdir, 'templates'), + os.path.join(self.testdir, 'templates')) + os.mkdir(os.path.join(self.testdir, 'etc')) + os.mkdir(os.path.join(self.testdir, 'lib')) + os.mkdir(os.path.join(self.testdir, 'lib', test.name)) + os.mkdir(os.path.join(self.testdir, 'log')) + + def generate_profile(self, global_opts, args_opts, name, addr, port): + newconf = ConfigParser.ConfigParser() + newconf.add_section('globals') + for k in global_opts.keys(): + newconf.set('globals', k, global_opts[k]) + newconf.add_section('arguments') + for k in args_opts.keys(): + newconf.set('arguments', k, args_opts[k]) + + profile = io.BytesIO() + newconf.write(profile) + + t = Template(profile.getvalue()) + text = t.substitute({'NAME': name, 'ADDRESS': addr, 'PORT': port, + 'TESTDIR': self.testdir, + 'ROOTDIR': self.rootdir, + 'TEST_USER': self.testuser}) + + filename = os.path.join(self.testdir, '%s_profile.cfg' % name) + with open(filename, 'wb') as f: + f.write(text) + + return filename + + def setup_http(self, name, addr, port): + httpdir = os.path.join(self.testdir, name) + os.mkdir(httpdir) + os.mkdir(os.path.join(httpdir, 'conf.d')) + os.mkdir(os.path.join(httpdir, 'html')) + os.mkdir(os.path.join(httpdir, 'logs')) + os.symlink('/etc/httpd/modules', os.path.join(httpdir, 'modules')) + + with open(os.path.join(self.rootdir, 'tests/httpd.conf')) as f: + t = Template(f.read()) + text = t.substitute({'HTTPROOT': httpdir, + 'HTTPADDR': addr, + 'HTTPPORT': port}) + filename = os.path.join(httpdir, 'httpd.conf') + with open(filename, 'w+') as f: + f.write(text) + + return filename + + def setup_idp_server(self, profile, name, addr, port, env): + http_conf_file = self.setup_http(name, addr, port) + cmd = [os.path.join(self.rootdir, + 'ipsilon/install/ipsilon-server-install'), + '--config-profile=%s' % profile] + subprocess.check_call(cmd, env=env) + os.symlink(os.path.join(self.rootdir, 'ipsilon'), + os.path.join(self.testdir, 'lib', name, 'ipsilon')) + + return http_conf_file + + def setup_sp_server(self, profile, name, addr, port, env): + http_conf_file = self.setup_http(name, addr, port) + cmd = [os.path.join(self.rootdir, + 'ipsilon/install/ipsilon-client-install'), + '--config-profile=%s' % profile] + subprocess.check_call(cmd, env=env) + + return http_conf_file + + def start_http_server(self, conf, env): + p = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND', '-f', conf], + env=env, preexec_fn=os.setsid) + self.processes.append(p) + + def wait(self): + for p in self.processes: + os.killpg(p.pid, signal.SIGTERM) + + def setup_servers(self, env=None): + raise NotImplementedError() + + def run(self, env): + exe = self.execname + if exe.endswith('c'): + exe = exe[:-1] + return subprocess.call([exe], env=env) diff --git a/tests/test1.cfg b/tests/test1.cfg deleted file mode 100644 index 01402f5..0000000 --- a/tests/test1.cfg +++ /dev/null @@ -1,37 +0,0 @@ -[tests] -servers=idp1:127.0.0.10:45080 -clients=sp1:127.0.0.11:45081 - -[idp1_globals] -TEMPLATES=${TESTDIR}/templates/install -CONFDIR=${TESTDIR}/etc -DATADIR=${TESTDIR}/lib -HTTPDCONFD=${TESTDIR}/idp1/conf.d -STATICDIR=${ROOTDIR} -BINDIR=${ROOTDIR}/ipsilon -WSGI_SOCKET_PREFIX=${TESTDIR}/idp1/logs/wsgi - -[idp1_arguments] -hostname=127.0.0.10:45080 -admin_user=${TEST_USER} -system_user=${TEST_USER} -instance=idp1 -secure=no -testauth=yes -pam=no -krb=no -ipa=no -server_debugging=True - -[sp1_globals] -HTTPDCONFD=${TESTDIR}/sp1/conf.d -SAML2_TEMPLATE=${TESTDIR}/templates/install/saml2/sp.conf -SAML2_CONFFILE=${TESTDIR}/sp1/conf.d/ipsilon-saml.conf -SAML2_HTTPDIR=${TESTDIR}/sp1/saml2 - -[sp1_arguments] -hostname=127.0.0.11:45081 -saml_idp_metadata=http://127.0.0.10:45080/idp1/saml2/metadata -saml_secure_setup=False -saml_auth=/sp -httpd_user=${TEST_USER} diff --git a/tests/test1.py b/tests/test1.py index 411ac6e..34b9c88 100755 --- a/tests/test1.py +++ b/tests/test1.py @@ -18,20 +18,104 @@ # along with this program. If not, see . -from helpers import http # pylint: disable=relative-import +from helpers.common import IpsilonTestBase # pylint: disable=relative-import +from helpers.http import HttpSessions # pylint: disable=relative-import import os import pwd import sys +from string import Template + + +idp_g = {'TEMPLATES': '${TESTDIR}/templates/install', + 'CONFDIR': '${TESTDIR}/etc', + 'DATADIR': '${TESTDIR}/lib', + 'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d', + 'STATICDIR': '${ROOTDIR}', + 'BINDIR': '${ROOTDIR}/ipsilon', + 'WSGI_SOCKET_PREFIX': '${TESTDIR}/${NAME}/logs/wsgi'} + + +idp_a = {'hostname': '${ADDRESS}:${PORT}', + 'admin_user': '${TEST_USER}', + 'system_user': '${TEST_USER}', + 'instance': '${NAME}', + 'secure': 'no', + 'testauth': 'yes', + 'pam': 'no', + 'krb': 'no', + 'ipa': 'no', + 'server_debugging': 'True'} + + +sp_g = {'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d', + 'SAML2_TEMPLATE': '${TESTDIR}/templates/install/saml2/sp.conf', + 'SAML2_CONFFILE': '${TESTDIR}/${NAME}/conf.d/ipsilon-saml.conf', + 'SAML2_HTTPDIR': '${TESTDIR}/${NAME}/saml2'} + + +sp_a = {'hostname': '${ADDRESS}:${PORT}', + 'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata', + 'saml_secure_setup': 'False', + 'saml_auth': '/sp', + 'httpd_user': '${TEST_USER}'} + + +def fixup_sp_httpd(httpdir): + location = """ + +Alias /sp ${HTTPDIR}/sp + + + Require all granted + +""" + index = """WORKS!""" + + t = Template(location) + text = t.substitute({'HTTPDIR': httpdir}) + with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f: + f.write(text) + + os.mkdir(httpdir + '/sp') + with open(httpdir + '/sp/index.html', 'w') as f: + f.write(index) + + +class IpsilonTest(IpsilonTestBase): + + def __init__(self): + super(IpsilonTest, self).__init__('test1', __file__) + + def setup_servers(self, env=None): + print "Installing IDP server" + name = 'idp1' + addr = '127.0.0.10' + port = '45080' + idp = self.generate_profile(idp_g, idp_a, name, addr, port) + conf = self.setup_idp_server(idp, name, addr, port, env) + + print "Starting IDP's httpd server" + self.start_http_server(conf, env) + + print "Installing SP server" + name = 'sp1' + addr = '127.0.0.11' + port = '45081' + sp = self.generate_profile(sp_g, sp_a, name, addr, port) + conf = self.setup_sp_server(sp, name, addr, port, env) + fixup_sp_httpd(os.path.dirname(conf)) + + print "Starting SP's httpd server" + self.start_http_server(conf, env) if __name__ == '__main__': - basedir = sys.argv[1] idpname = 'idp1' spname = 'sp1' user = pwd.getpwuid(os.getuid())[0] - sess = http.HttpSessions() + sess = HttpSessions() sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon') sess.add_server(spname, 'http://127.0.0.11:45081') diff --git a/tests/tests.py b/tests/tests.py index 6fa880d..4690442 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -18,21 +18,27 @@ # along with this program. If not, see . import argparse -import ConfigParser from datetime import datetime +import inspect +from ipsilon.util import plugin import logging import os -import pwd -import shutil -import signal -import subprocess import sys -from string import Template +import subprocess +import traceback logger = None +class Tests(object): + + def __init__(self): + p = plugin.Plugins() + (pathname, dummy) = os.path.split(inspect.getfile(Tests)) + self.plugins = p.get_plugins(pathname, 'IpsilonTest') + + def parse_args(): parser = argparse.ArgumentParser(description='Ipsilon Tests Environment') parser.add_argument('--path', default='%s/testdir' % os.getcwd(), @@ -46,12 +52,12 @@ def parse_args(): return vars(parser.parse_args()) -def openlogs(path, test): +def openlogs(path, name): global logger # pylint: disable=W0603 logger = logging.getLogger() try: datestr = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") - filename = '%s/test-%s-%s.log' % (path, test, datestr) + filename = '%s/test-%s-%s.log' % (path, name, datestr) lh = logging.FileHandler(filename) except IOError, e: print >> sys.stderr, 'Unable to open %s (%s)' % (filename, str(e)) @@ -62,115 +68,6 @@ def openlogs(path, test): logger.setLevel(logging.DEBUG) -def force_remove(op, name, info): - os.chmod(name, 0700) - os.remove(name) - - -def setup_http(httpdir, addr, port): - os.mkdir(httpdir) - os.mkdir(httpdir + '/conf.d') - os.mkdir(httpdir + '/html') - os.mkdir(httpdir + '/logs') - os.symlink('/etc/httpd/modules', httpdir + '/modules') - - with open('tests/httpd.conf') as f: - t = Template(f.read()) - text = t.substitute({'HTTPROOT': httpdir, - 'HTTPADDR': addr, 'HTTPPORT': port}) - with open(httpdir + '/httpd.conf', 'w+') as f: - f.write(text) - - -def setup_test(path, test): - profile = 'tests/%s.cfg' % test - if not os.path.exists(profile): - raise ValueError('Unrecognized test name [%s]' % test) - - opts = {} - config = ConfigParser.ConfigParser() - config.read(profile) - if 'tests' not in config.sections(): - raise ValueError('Missing [tests] in profile [%s]' % test) - T = config.options('tests') - for t in T: - opts[t] = config.get('tests', t) - - base = '%s/%s' % (path, test) - if os.path.exists(base): - shutil.rmtree(base, onerror=force_remove) - os.makedirs(base) - shutil.copytree('templates', base + '/templates') - os.mkdir(base + '/etc') - os.mkdir(base + '/lib') - os.mkdir(base + '/lib/' + test) - os.mkdir(base + '/log') - - with open(profile) as f: - t = Template(f.read()) - text = t.substitute({'TESTDIR': base, 'ROOTDIR': os.getcwd(), - 'TEST_USER': pwd.getpwuid(os.getuid())[0]}) - with open(base + '/profile.cfg', 'w+') as f: - f.write(text) - - opts['basedir'] = base - return opts - - -def generate_profile(profile, name): - config = ConfigParser.ConfigParser() - config.read(profile) - - global_section = '%s_globals' % name - global_options = {} - if global_section in config.sections(): - G = config.options(global_section) - for g in G: - global_options[g] = config.get(global_section, g) - - args_section = '%s_arguments' % name - args_options = {} - if args_section in config.sections(): - A = config.options(args_section) - for a in A: - args_options[a] = config.get(args_section, a) - - newconf = ConfigParser.ConfigParser() - newconf.add_section('globals') - for k in global_options.keys(): - newconf.set('globals', k, global_options[k]) - newconf.add_section('arguments') - for k in args_options.keys(): - newconf.set('arguments', k, args_options[k]) - - filename = os.path.join(os.path.dirname(profile), '%s_profile.cfg' % name) - with open(filename, 'wb') as f: - newconf.write(f) - - return filename - - -def fixup_sp_httpd(httpdir): - location = """ - -Alias /sp ${HTTPDIR}/sp - - - Require all granted - -""" - index = """WORKS!""" - - t = Template(location) - text = t.substitute({'HTTPDIR': httpdir}) - with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f: - f.write(text) - - os.mkdir(httpdir + '/sp') - with open(httpdir + '/sp/index.html', 'w') as f: - f.write(index) - - def try_wrappers(base, wrappers): if wrappers == 'no': return {} @@ -192,83 +89,38 @@ def try_wrappers(base, wrappers): return wenv + if __name__ == '__main__': args = parse_args() + tests = Tests() + if args['test'] not in tests.plugins: + print >> sys.stderr, "Unknown test [%s]" % args['test'] + sys.exit(1) + test = tests.plugins[args['test']] + if not os.path.exists(args['path']): os.makedirs(args['path']) + openlogs(args['path'], args['test']) - options = setup_test(args['path'], args['test']) - basedir = options['basedir'] + test.setup_base(args['path'], test) - env = try_wrappers(basedir, args['wrappers']) - env['PYTHONPATH'] = './' + env = try_wrappers(test.testdir, args['wrappers']) + env['PYTHONPATH'] = test.rootdir - srvs = [] try: - for h in options['servers'].split(','): - sname, saddr, sport = h.split(':') - basehttpdir = '%s/%s' % (basedir, sname) - setup_http(basehttpdir, saddr, sport) - - print "Installing IDP server %s" % sname - sprofile = generate_profile('%s/profile.cfg' % basedir, sname) - p = subprocess.Popen(['./ipsilon/install/ipsilon-server-install', - '--config-profile=%s' % sprofile], env=env, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - logger.error(stderr) - logger.info(stdout) - if p.returncode: - sys.exit(p.returncode) - - os.symlink('%s/ipsilon' % os.getcwd(), - '%s/lib/%s/ipsilon' % (basedir, sname)) - - print "Starting httpd server in %s" % basehttpdir - srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND', - '-f', basehttpdir + '/httpd.conf'], - env=env, preexec_fn=os.setsid) - srvs.append(srv) - - for h in options['clients'].split(','): - sname, saddr, sport = h.split(':') - basehttpdir = '%s/%s' % (basedir, sname) - setup_http(basehttpdir, saddr, sport) - - print "Installing SP server %s" % sname - sprofile = generate_profile('%s/profile.cfg' % basedir, sname) - p = subprocess.Popen(['./ipsilon/install/ipsilon-client-install', - '--config-profile=%s' % sprofile], env=env, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - logger.error(stderr) - logger.info(stdout) - if p.returncode: - sys.exit(p.returncode) - - fixup_sp_httpd(basehttpdir) - - print "Starting httpd server in %s" % basehttpdir - srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND', - '-f', basehttpdir + '/httpd.conf'], - env=env, preexec_fn=os.setsid) - srvs.append(srv) - - print "Testing installation" - if os.path.exists('tests/%s.py' % args['test']): - code = subprocess.call(['./tests/%s.py' % args['test'], basedir], - env=env) - if code: - sys.exit(code) - except Exception: # pylint: disable=broad-except + test.setup_servers(env) + + code = test.run(env) + if code: + sys.exit(code) + except Exception, e: # pylint: disable=broad-except + print >> sys.stderr, "Error: %s" % repr(e) + traceback.print_exc(None, sys.stderr) sys.exit(1) finally: - for srv in srvs: - os.killpg(srv.pid, signal.SIGTERM) + test.wait() print "FINISHED" -- cgit