~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to buildmailman.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-12-22 04:55:30 UTC
  • mfrom: (14577.1.1 testfix)
  • Revision ID: launchpad@pqm.canonical.com-20111222045530-wki9iu6c0ysqqwkx
[r=wgrant][no-qa] Fix test_publisherconfig lpstorm import. Probably a
        silent conflict between megalint and apocalypse.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
#
 
3
# Copyright 2009, 2010 Canonical Ltd.  This software is licensed under the
 
4
# GNU Affero General Public License version 3 (see the file LICENSE).
 
5
 
 
6
import os
 
7
import grp
 
8
import pwd
 
9
import sys
 
10
import errno
 
11
import socket
 
12
import tempfile
 
13
import subprocess
 
14
 
 
15
from canonical.config import config
 
16
from lp.services.mailman.config import (
 
17
    configure_prefix, configure_siteowner)
 
18
from lp.services.mailman.monkeypatches import monkey_patch
 
19
from lazr.config import as_username_groupname
 
20
 
 
21
basepath = [part for part in sys.path if part]
 
22
 
 
23
 
 
24
def build_mailman():
 
25
    # Build and install Mailman if it is enabled and not yet built.
 
26
    if not config.mailman.build:
 
27
        # There's nothing to do.
 
28
        return 0
 
29
    mailman_path = configure_prefix(config.mailman.build_prefix)
 
30
    mailman_bin = os.path.join(mailman_path, 'bin')
 
31
    var_dir = os.path.abspath(config.mailman.build_var_dir)
 
32
 
 
33
    # If we can import the package, we assume Mailman is properly built and
 
34
    # installed.  This does not catch re-installs that might be necessary
 
35
    # should our copy in sourcecode be updated.  Do that manually.
 
36
    sys.path.append(mailman_path)
 
37
    try:
 
38
        import Mailman
 
39
        # Also check for Launchpad-specific bits stuck into the source tree by
 
40
        # monkey_patch(), in case this is half-installed.  See
 
41
        # <https://bugs.launchpad.net/launchpad-registry/+bug/683486>.
 
42
        from Mailman.Queue import XMLRPCRunner
 
43
        from Mailman.Handlers import LPModerate
 
44
    except ImportError:
 
45
        pass
 
46
    else:
 
47
        return 0
 
48
 
 
49
    # sys.path_importer_cache is a mapping of elements of sys.path to importer
 
50
    # objects used to handle them. In Python2.5+ when an element of sys.path
 
51
    # is found to not exist on disk, a NullImporter is created and cached -
 
52
    # this causes Python to never bother re-inspecting the disk for that path
 
53
    # element. We must clear that cache element so that our second attempt to
 
54
    # import MailMan after building it will actually check the disk.
 
55
    del sys.path_importer_cache[mailman_path]
 
56
 
 
57
    # Make sure the target directories exist and have the correct
 
58
    # permissions, otherwise configure will complain.
 
59
    user, group = as_username_groupname(config.mailman.build_user_group)
 
60
    # Now work backwards to get the uid and gid
 
61
    try:
 
62
        uid = pwd.getpwnam(user).pw_uid
 
63
    except KeyError:
 
64
        print >> sys.stderr, 'No user found:', user
 
65
        sys.exit(1)
 
66
    try:
 
67
        gid = grp.getgrnam(group).gr_gid
 
68
    except KeyError:
 
69
        print >> sys.stderr, 'No group found:', group
 
70
        sys.exit(1)
 
71
 
 
72
    # Ensure that the var_dir exists, is owned by the user:group, and has
 
73
    # the necessary permissions.  Set the mode separately after the
 
74
    # makedirs() call because some platforms ignore mkdir()'s mode (though
 
75
    # I think Linux does not ignore it -- better safe than sorry).
 
76
    try:
 
77
        os.makedirs(var_dir)
 
78
    except OSError, e:
 
79
        if e.errno != errno.EEXIST:
 
80
            raise
 
81
    os.chown(var_dir, uid, gid)
 
82
    os.chmod(var_dir, 02775)
 
83
 
 
84
    mailman_source = os.path.join('sourcecode', 'mailman')
 
85
    if config.mailman.build_host_name:
 
86
        build_host_name = config.mailman.build_host_name
 
87
    else:
 
88
        build_host_name = socket.getfqdn()
 
89
 
 
90
    # Build and install the Mailman software.  Note that we don't care about
 
91
    # --with-cgi-gid because we're not going to use that Mailman subsystem.
 
92
    executable = os.path.abspath('bin/py')
 
93
    configure_args = (
 
94
        './configure',
 
95
        '--prefix', mailman_path,
 
96
        '--with-var-prefix=' + var_dir,
 
97
        '--with-python=' + executable,
 
98
        '--with-username=' + user,
 
99
        '--with-groupname=' + group,
 
100
        '--with-mail-gid=' + group,
 
101
        '--with-mailhost=' + build_host_name,
 
102
        '--with-urlhost=' + build_host_name,
 
103
        )
 
104
    # Configure.
 
105
    retcode = subprocess.call(configure_args, cwd=mailman_source)
 
106
    if retcode:
 
107
        print >> sys.stderr, 'Could not configure Mailman:'
 
108
        sys.exit(retcode)
 
109
    # Make.
 
110
    retcode = subprocess.call(('make', ), cwd=mailman_source)
 
111
    if retcode:
 
112
        print >> sys.stderr, 'Could not make Mailman.'
 
113
        sys.exit(retcode)
 
114
    # We have a brief interlude before we install.  Hardy will not
 
115
    # accept a script as the executable for the shebang line--it will
 
116
    # treat the file as a shell script instead. The ``bin/by``
 
117
    # executable that we specified in '--with-python' above is a script
 
118
    # so this behavior causes problems for us. Our work around is to
 
119
    # prefix the ``bin/py`` script with ``/usr/bin/env``, which makes
 
120
    # Hardy happy.  We need to do this before we install because the
 
121
    # installation will call Mailman's ``bin/update``, which is a script
 
122
    # that needs this fix.
 
123
    build_dir = os.path.join(mailman_source, 'build')
 
124
    original = '#! %s\n' % (executable, )
 
125
    modified = '#! /usr/bin/env %s\n' % (executable, )
 
126
    for (dirpath, dirnames, filenames) in os.walk(build_dir):
 
127
        for filename in filenames:
 
128
            filename = os.path.join(dirpath, filename)
 
129
            f = open(filename, 'r')
 
130
            if f.readline() == original:
 
131
                rest = f.read()
 
132
                f.close()
 
133
                f = open(filename, 'w')
 
134
                f.write(modified)
 
135
                f.write(rest)
 
136
            f.close()
 
137
    # Now we actually install.
 
138
    retcode = subprocess.call(('make', 'install'), cwd=mailman_source)
 
139
    if retcode:
 
140
        print >> sys.stderr, 'Could not install Mailman.'
 
141
        sys.exit(retcode)
 
142
    # Try again to import the package.
 
143
    try:
 
144
        # pylint: disable-msg=W0404
 
145
        import Mailman
 
146
    except ImportError:
 
147
        print >> sys.stderr, 'Could not import the Mailman package'
 
148
        return 1
 
149
 
 
150
    # Check to see if the site list exists.  The output can go to /dev/null
 
151
    # because we don't really care about it.  The site list exists if
 
152
    # config_list returns a zero exit status, otherwise it doesn't
 
153
    # (probably).  Before we can do this however, we must monkey patch
 
154
    # Mailman, otherwise mm_cfg.py won't be set up correctly.
 
155
    monkey_patch(mailman_path, config)
 
156
 
 
157
    import Mailman.mm_cfg
 
158
    retcode = subprocess.call(
 
159
        ('./config_list', '-o', '/dev/null',
 
160
         Mailman.mm_cfg.MAILMAN_SITE_LIST),
 
161
        cwd=mailman_bin,
 
162
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
163
 
 
164
    if retcode:
 
165
        addr, password = configure_siteowner(
 
166
            config.mailman.build_site_list_owner)
 
167
 
 
168
        # The site list does not yet exist, so create it now.
 
169
        retcode = subprocess.call(
 
170
            ('./newlist', '--quiet',
 
171
             '--emailhost=' + build_host_name,
 
172
             Mailman.mm_cfg.MAILMAN_SITE_LIST,
 
173
             addr, password),
 
174
            cwd=mailman_bin)
 
175
        if retcode:
 
176
            print >> sys.stderr, 'Could not create site list'
 
177
            return retcode
 
178
 
 
179
    retcode = configure_site_list(
 
180
        mailman_bin, Mailman.mm_cfg.MAILMAN_SITE_LIST)
 
181
    if retcode:
 
182
        print >> sys.stderr, 'Could not configure site list'
 
183
        return retcode
 
184
 
 
185
    # Create a directory to hold the gzip'd tarballs for the directories of
 
186
    # deactivated lists.
 
187
    try:
 
188
        os.mkdir(os.path.join(Mailman.mm_cfg.VAR_PREFIX, 'backups'))
 
189
    except OSError, e:
 
190
        if e.errno != errno.EEXIST:
 
191
            raise
 
192
 
 
193
    return 0
 
194
 
 
195
 
 
196
def configure_site_list(mailman_bin, site_list_name):
 
197
    """Configure the site list.
 
198
 
 
199
    Currently, the only thing we want to set is to not advertise the
 
200
    site list.
 
201
    """
 
202
    fd, config_file_name = tempfile.mkstemp()
 
203
    try:
 
204
        os.close(fd)
 
205
        config_file = open(config_file_name, 'w')
 
206
        try:
 
207
            print >> config_file, 'advertised = False'
 
208
        finally:
 
209
            config_file.close()
 
210
        return subprocess.call(
 
211
            ('./config_list', '-i', config_file_name, site_list_name),
 
212
            cwd=mailman_bin)
 
213
    finally:
 
214
        os.remove(config_file_name)
 
215
 
 
216
 
 
217
def main():
 
218
    # setting python paths
 
219
    program = sys.argv[0]
 
220
 
 
221
    src = 'lib'
 
222
    here = os.path.dirname(os.path.abspath(program))
 
223
    srcdir = os.path.join(here, src)
 
224
    sys.path = [srcdir, here] + basepath
 
225
    return build_mailman()
 
226
 
 
227
 
 
228
if __name__ == '__main__':
 
229
    return_code = main()
 
230
    sys.exit(return_code)