Print more info about the steps being performed
[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     parser.add_argument('--wrappers', default='auto',
43                         choices=['yes', 'no', 'auto'],
44                         help="Run the tests with socket wrappers")
45
46     return vars(parser.parse_args())
47
48
49 def openlogs(path, test):
50     global logger  # pylint: disable=W0603
51     logger = logging.getLogger()
52     try:
53         datestr = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
54         filename = '%s/test-%s-%s.log' % (path, test, datestr)
55         lh = logging.FileHandler(filename)
56     except IOError, e:
57         print >> sys.stderr, 'Unable to open %s (%s)' % (filename, str(e))
58         lh = logging.StreamHandler(sys.stderr)
59     formatter = logging.Formatter('[%(asctime)s] %(message)s')
60     lh.setFormatter(formatter)
61     logger.addHandler(lh)
62     logger.setLevel(logging.DEBUG)
63
64
65 def force_remove(op, name, info):
66     os.chmod(name, 0700)
67     os.remove(name)
68
69
70 def setup_http(httpdir, addr, port):
71     os.mkdir(httpdir)
72     os.mkdir(httpdir + '/conf.d')
73     os.mkdir(httpdir + '/html')
74     os.mkdir(httpdir + '/logs')
75     os.symlink('/etc/httpd/modules', httpdir + '/modules')
76
77     with open('tests/httpd.conf') as f:
78         t = Template(f.read())
79         text = t.substitute({'HTTPROOT': httpdir,
80                              'HTTPADDR': addr, 'HTTPPORT': port})
81     with open(httpdir + '/httpd.conf', 'w+') as f:
82         f.write(text)
83
84
85 def setup_test(path, test):
86     profile = 'tests/%s.cfg' % test
87     if not os.path.exists(profile):
88         raise ValueError('Unrecognized test name [%s]' % test)
89
90     opts = {}
91     config = ConfigParser.ConfigParser()
92     config.read(profile)
93     if 'tests' not in config.sections():
94         raise ValueError('Missing [tests] in profile [%s]' % test)
95     T = config.options('tests')
96     for t in T:
97         opts[t] = config.get('tests', t)
98
99     base = '%s/%s' % (path, test)
100     if os.path.exists(base):
101         shutil.rmtree(base, onerror=force_remove)
102     os.makedirs(base)
103     shutil.copytree('templates', base + '/templates')
104     os.mkdir(base + '/etc')
105     os.mkdir(base + '/lib')
106     os.mkdir(base + '/lib/' + test)
107     os.mkdir(base + '/log')
108
109     with open(profile) as f:
110         t = Template(f.read())
111         text = t.substitute({'TESTDIR': base, 'ROOTDIR': os.getcwd(),
112                              'TEST_USER': pwd.getpwuid(os.getuid())[0]})
113     with open(base + '/profile.cfg', 'w+') as f:
114         f.write(text)
115
116     opts['basedir'] = base
117     return opts
118
119
120 def generate_profile(profile, name):
121     config = ConfigParser.ConfigParser()
122     config.read(profile)
123
124     global_section = '%s_globals' % name
125     global_options = {}
126     if global_section in config.sections():
127         G = config.options(global_section)
128         for g in G:
129             global_options[g] = config.get(global_section, g)
130
131     args_section = '%s_arguments' % name
132     args_options = {}
133     if args_section in config.sections():
134         A = config.options(args_section)
135         for a in A:
136             args_options[a] = config.get(args_section, a)
137
138     newconf = ConfigParser.ConfigParser()
139     newconf.add_section('globals')
140     for k in global_options.keys():
141         newconf.set('globals', k, global_options[k])
142     newconf.add_section('arguments')
143     for k in args_options.keys():
144         newconf.set('arguments', k, args_options[k])
145
146     filename = os.path.join(os.path.dirname(profile), '%s_profile.cfg' % name)
147     with open(filename, 'wb') as f:
148         newconf.write(f)
149
150     return filename
151
152
153 def fixup_sp_httpd(httpdir):
154     location = """
155
156 Alias /sp ${HTTPDIR}/sp
157
158 <Directory ${HTTPDIR}/sp>
159     Require all granted
160 </Directory>
161 """
162     index = """WORKS!"""
163
164     t = Template(location)
165     text = t.substitute({'HTTPDIR': httpdir})
166     with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
167         f.write(text)
168
169     os.mkdir(httpdir + '/sp')
170     with open(httpdir + '/sp/index.html', 'w') as f:
171         f.write(index)
172
173
174 def try_wrappers(base, wrappers):
175     if wrappers == 'no':
176         return {}
177
178     pkgcfg = subprocess.Popen(['pkg-config', '--exists', 'socket_wrapper'])
179     pkgcfg.wait()
180     if pkgcfg.returncode != 0:
181         if wrappers == 'auto':
182             return {}
183         else:
184             raise ValueError('Socket Wrappers not available')
185
186     wrapdir = os.path.join(base, 'wrapdir')
187     os.mkdir(wrapdir)
188
189     wenv = {'LD_PRELOAD': 'libsocket_wrapper.so',
190             'SOCKET_WRAPPER_DIR': wrapdir,
191             'SOCKET_WRAPPER_DEFAULT_IFACE': '9'}
192
193     return wenv
194
195 if __name__ == '__main__':
196
197     args = parse_args()
198
199     if not os.path.exists(args['path']):
200         os.makedirs(args['path'])
201     openlogs(args['path'], args['test'])
202
203     options = setup_test(args['path'], args['test'])
204     basedir = options['basedir']
205
206     env = try_wrappers(basedir, args['wrappers'])
207     env['PYTHONPATH'] = './'
208
209     srvs = []
210     try:
211         for h in options['servers'].split(','):
212             sname, saddr, sport = h.split(':')
213             basehttpdir = '%s/%s' % (basedir, sname)
214             setup_http(basehttpdir, saddr, sport)
215
216             print "Installing IDP server %s" % sname
217             sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
218             p = subprocess.Popen(['./ipsilon/install/ipsilon-server-install',
219                                   '--config-profile=%s' % sprofile], env=env,
220                                  stdout=subprocess.PIPE,
221                                  stderr=subprocess.PIPE)
222             stdout, stderr = p.communicate()
223             logger.error(stderr)
224             logger.info(stdout)
225             if p.returncode:
226                 sys.exit(p.returncode)
227
228             os.symlink('%s/ipsilon' % os.getcwd(),
229                        '%s/lib/%s/ipsilon' % (basedir, sname))
230
231             print "Starting httpd server in %s" % basehttpdir
232             srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
233                                     '-f', basehttpdir + '/httpd.conf'],
234                                    env=env, preexec_fn=os.setsid)
235             srvs.append(srv)
236
237         for h in options['clients'].split(','):
238             sname, saddr, sport = h.split(':')
239             basehttpdir = '%s/%s' % (basedir, sname)
240             setup_http(basehttpdir, saddr, sport)
241
242             print "Installing SP server %s" % sname
243             sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
244             p = subprocess.Popen(['./ipsilon/install/ipsilon-client-install',
245                                   '--config-profile=%s' % sprofile], env=env,
246                                  stdout=subprocess.PIPE,
247                                  stderr=subprocess.PIPE)
248             stdout, stderr = p.communicate()
249             logger.error(stderr)
250             logger.info(stdout)
251             if p.returncode:
252                 sys.exit(p.returncode)
253
254             fixup_sp_httpd(basehttpdir)
255
256             print "Starting httpd server in %s" % basehttpdir
257             srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
258                                     '-f', basehttpdir + '/httpd.conf'],
259                                    env=env, preexec_fn=os.setsid)
260             srvs.append(srv)
261
262         print "Testing installation"
263         if os.path.exists('tests/%s.py' % args['test']):
264             code = subprocess.call(['./tests/%s.py' % args['test'], basedir],
265                                    env=env)
266             if code:
267                 sys.exit(code)
268     except Exception:  # pylint: disable=broad-except
269         sys.exit(1)
270     finally:
271         for srv in srvs:
272             os.killpg(srv.pid, signal.SIGTERM)
273
274     print "FINISHED"