Add basic testing infrastructure
[cascardo/ipsilon.git] / tests / tests.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import argparse
21 import ConfigParser
22 from datetime import datetime
23 import logging
24 import os
25 import pwd
26 import shutil
27 import signal
28 import subprocess
29 import sys
30 from string import Template
31
32
33 logger = None
34
35
36 def parse_args():
37     parser = argparse.ArgumentParser(description='Ipsilon Tests Environment')
38     parser.add_argument('--path', default='%s/testdir' % os.getcwd(),
39                         help="Directory in which tests are run")
40     parser.add_argument('--test', default='test1',
41                         help="The test to run")
42
43     return vars(parser.parse_args())
44
45
46 def openlogs(path, test):
47     global logger  # pylint: disable=W0603
48     logger = logging.getLogger()
49     try:
50         datestr = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
51         filename = '%s/test-%s-%s.log' % (path, test, datestr)
52         lh = logging.FileHandler(filename)
53     except IOError, e:
54         print >> sys.stderr, 'Unable to open %s (%s)' % (filename, str(e))
55         lh = logging.StreamHandler(sys.stderr)
56     formatter = logging.Formatter('[%(asctime)s] %(message)s')
57     lh.setFormatter(formatter)
58     logger.addHandler(lh)
59     logger.setLevel(logging.DEBUG)
60
61
62 def force_remove(op, name, info):
63     os.chmod(name, 0700)
64     os.remove(name)
65
66
67 def setup_http(httpdir, addr, port):
68     os.mkdir(httpdir)
69     os.mkdir(httpdir + '/conf.d')
70     os.mkdir(httpdir + '/html')
71     os.mkdir(httpdir + '/logs')
72     os.symlink('/etc/httpd/modules', httpdir + '/modules')
73
74     with open('tests/httpd.conf') as f:
75         t = Template(f.read())
76         text = t.substitute({'HTTPROOT': httpdir,
77                              'HTTPADDR': addr, 'HTTPPORT': port})
78     with open(httpdir + '/httpd.conf', 'w+') as f:
79         f.write(text)
80
81
82 def setup_test(path, test):
83     profile = 'tests/%s.cfg' % test
84     if not os.path.exists(profile):
85         raise ValueError('Unrecognized test name [%s]' % test)
86
87     opts = {}
88     config = ConfigParser.ConfigParser()
89     config.read(profile)
90     if 'tests' not in config.sections():
91         raise ValueError('Missing [tests] in profile [%s]' % test)
92     T = config.options('tests')
93     for t in T:
94         opts[t] = config.get('tests', t)
95
96     base = '%s/%s' % (path, test)
97     if os.path.exists(base):
98         shutil.rmtree(base, onerror=force_remove)
99     os.makedirs(base)
100     shutil.copytree('templates', base + '/templates')
101     os.mkdir(base + '/etc')
102     os.mkdir(base + '/lib')
103     os.mkdir(base + '/lib/' + test)
104     os.mkdir(base + '/log')
105
106     with open(profile) as f:
107         t = Template(f.read())
108         text = t.substitute({'TESTDIR': base, 'ROOTDIR': os.getcwd(),
109                              'TEST_USER': pwd.getpwuid(os.getuid())[0]})
110     with open(base + '/profile.cfg', 'w+') as f:
111         f.write(text)
112
113     opts['basedir'] = base
114     return opts
115
116
117 def generate_profile(profile, name):
118     config = ConfigParser.ConfigParser()
119     config.read(profile)
120
121     global_section = '%s_globals' % name
122     global_options = {}
123     if global_section in config.sections():
124         G = config.options(global_section)
125         for g in G:
126             global_options[g] = config.get(global_section, g)
127
128     args_section = '%s_arguments' % name
129     args_options = {}
130     if args_section in config.sections():
131         A = config.options(args_section)
132         for a in A:
133             args_options[a] = config.get(args_section, a)
134
135     newconf = ConfigParser.ConfigParser()
136     newconf.add_section('globals')
137     for k in global_options.keys():
138         newconf.set('globals', k, global_options[k])
139     newconf.add_section('arguments')
140     for k in args_options.keys():
141         newconf.set('arguments', k, args_options[k])
142
143     filename = os.path.join(os.path.dirname(profile), '%s_profile.cfg' % name)
144     with open(filename, 'wb') as f:
145         newconf.write(f)
146
147     return filename
148
149
150 def fixup_sp_httpd(httpdir):
151     location = """
152
153 Alias /sp ${HTTPDIR}/sp
154
155 <Directory ${HTTPDIR}/sp>
156     Require all granted
157 </Directory>
158 """
159     index = """WORKS!"""
160
161     t = Template(location)
162     text = t.substitute({'HTTPDIR': httpdir})
163     with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
164         f.write(text)
165
166     os.mkdir(httpdir + '/sp')
167     with open(httpdir + '/sp/index.html', 'w') as f:
168         f.write(index)
169
170 if __name__ == '__main__':
171
172     args = parse_args()
173
174     if not os.path.exists(args['path']):
175         os.makedirs(args['path'])
176     openlogs(args['path'], args['test'])
177
178     options = setup_test(args['path'], args['test'])
179     basedir = options['basedir']
180
181     env={'PYTHONPATH':'./'}
182     srvs = []
183     try:
184         for h in options['servers'].split(','):
185             sname, saddr, sport = h.split(':')
186             basehttpdir = '%s/%s' % (basedir, sname)
187             setup_http(basehttpdir, saddr, sport)
188
189             sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
190             p = subprocess.Popen(['./ipsilon/install/ipsilon-server-install',
191                                   '--config-profile=%s' % sprofile], env=env,
192                                  stdout=subprocess.PIPE,
193                                  stderr=subprocess.PIPE)
194             stdout, stderr = p.communicate()
195             logger.error(stderr)
196             logger.info(stdout)
197             if p.returncode:
198                 sys.exit(p.returncode)
199
200             os.symlink('%s/ipsilon' % os.getcwd(),
201                        '%s/lib/%s/ipsilon' % (basedir, sname))
202
203             print "Starting httpd server in %s" % basehttpdir
204             srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
205                                     '-f', basehttpdir + '/httpd.conf'],
206                                    env=env, preexec_fn=os.setsid)
207             srvs.append(srv)
208
209         for h in options['clients'].split(','):
210             sname, saddr, sport = h.split(':')
211             basehttpdir = '%s/%s' % (basedir, sname)
212             setup_http(basehttpdir, saddr, sport)
213
214             sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
215             p = subprocess.Popen(['./ipsilon/install/ipsilon-client-install',
216                                   '--config-profile=%s' % sprofile], env=env,
217                                  stdout=subprocess.PIPE,
218                                  stderr=subprocess.PIPE)
219             stdout, stderr = p.communicate()
220             logger.error(stderr)
221             logger.info(stdout)
222             if p.returncode:
223                 sys.exit(p.returncode)
224
225             fixup_sp_httpd(basehttpdir)
226
227             print "Starting httpd server in %s" % basehttpdir
228             srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
229                                     '-f', basehttpdir + '/httpd.conf'],
230                                    env=env, preexec_fn=os.setsid)
231             srvs.append(srv)
232
233         if os.path.exists('tests/%s.py' % args['test']):
234             code = subprocess.call(['./tests/%s.py' % args['test'], basedir],
235                                    env=env)
236             if code:
237                 sys.exit(code)
238     except Exception:  # pylint: disable=broad-except
239         sys.exit(1)
240     finally:
241         for srv in srvs:
242             os.killpg(srv.pid, signal.SIGTERM)
243
244     print "FINISHED"