bootstrap: Install correct python modules on CentOS7
[bbaumbach/samba-autobuild/.git] / bootstrap / config.py
1 #!/usr/bin/env python3
2
3 # Copyright (C) Catalyst.Net Ltd 2019
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 """
19 Manage dependencies and bootstrap environments for Samba.
20
21 Config file for packages and templates.
22
23 Author: Joe Guo <joeg@catalyst.net.nz>
24 """
25 import os
26 from os.path import abspath, dirname, join
27 HERE = abspath(dirname(__file__))
28 # output dir for rendered files
29 OUT = join(HERE, 'generated-dists')
30
31
32 # pkgs with same name in all packaging systems
33 COMMON = [
34     'acl',
35     'attr',
36     'autoconf',
37     'binutils',
38     'bison',
39     'curl',
40     'flex',
41     'gcc',
42     'gdb',
43     'git',
44     'gzip',
45     'hostname',
46     'htop',
47     'lcov',
48     'make',
49     'patch',
50     'perl',
51     'psmisc',  # for pstree in test
52     'rng-tools',
53     'rsync',
54     'sed',
55     'sudo',  # docker images has no sudo by default
56     'tar',
57     'tree',
58 ]
59
60
61 # define pkgs for all packaging systems in parallel
62 # make it easier to find missing ones
63 # use latest ubuntu and fedora as defaults
64 # deb, rpm, ...
65 PKGS = [
66     # NAME1-dev, NAME2-devel
67     ('lmdb-utils', 'lmdb'),
68     ('nettle-dev', 'nettle-devel'),
69     ('zlib1g-dev', 'zlib-devel'),
70     ('libbsd-dev', 'libbsd-devel'),
71     ('libaio-dev', 'libaio-devel'),
72     ('libarchive-dev', 'libarchive-devel'),
73     ('libblkid-dev', 'libblkid-devel'),
74     ('libcap-dev', 'libcap-devel'),
75     ('libacl1-dev', 'libacl-devel'),
76     ('libattr1-dev', 'libattr-devel'),
77
78     # libNAME1-dev, NAME2-devel
79     ('libpopt-dev', 'popt-devel'),
80     ('libreadline-dev', 'readline-devel'),
81     ('libjansson-dev', 'jansson-devel'),
82     ('liblmdb-dev', 'lmdb-devel'),
83     ('libncurses5-dev', 'ncurses-devel'),
84     # NOTE: Debian 7+ or Ubuntu 16.04+
85     ('libsystemd-dev', 'systemd-devel'),
86     ('libkrb5-dev', 'krb5-devel'),
87     ('libldap2-dev', 'openldap-devel'),
88     ('libcups2-dev', 'cups-devel'),
89     ('libpam0g-dev', 'pam-devel'),
90     ('libgpgme11-dev', 'gpgme-devel'),
91     # NOTE: Debian 8+ and Ubuntu 14.04+
92     ('libgnutls28-dev', 'gnutls-devel'),
93     ('libtasn1-bin', ''),
94     ('libtasn1-dev', 'libtasn1-devel'),
95     ('', 'quota-devel'),
96     ('uuid-dev', 'libuuid-devel'),
97     ('libjs-jquery', ''),
98     ('libavahi-common-dev', 'avahi-devel'),
99     ('libdbus-1-dev', 'dbus-devel'),
100     ('libpcap-dev', 'libpcap-devel'),
101     ('libunwind-dev', 'libunwind-devel'),  # for back trace
102     ('libglib2.0-dev', 'glib2-devel'),
103     ('libicu-dev', 'libicu-devel'),
104
105     # NAME1, NAME2
106     # for debian, locales provide locale support with language packs
107     # ubuntu split language packs to language-pack-xx
108     # for centos, glibc-common provide locale support with language packs
109     # fedora split language packs  to glibc-langpack-xx
110     ('locales', 'glibc-common'),  # required for locale
111     ('language-pack-en', 'glibc-langpack-en'),  # we need en_US.UTF-8
112     ('bind9utils', 'bind-utils'),
113     ('dnsutils', ''),
114     ('xsltproc', 'libxslt'),
115     ('krb5-user', ''),
116     ('krb5-config', ''),
117     ('krb5-kdc', 'krb5-server'),
118     ('apt-utils', 'yum-utils'),
119     ('pkg-config', 'pkgconfig'),
120     ('procps', 'procps-ng'),  # required for the free cmd in tests
121     ('lsb-release', 'lsb-release'),  # we need lsb_relase to show info
122     ('', 'rpcgen'),  # required for test
123     # refer: https://fedoraproject.org/wiki/Changes/SunRPCRemoval
124     ('', 'libtirpc-devel'),  # for <rpc/rpc.h> header on fedora
125     ('', 'libnsl2-devel'),  # for <rpcsvc/yp_prot.h> header on fedora
126     ('', 'rpcsvc-proto-devel'), # for <rpcsvc/rquota.h> header
127     ('mawk', 'gawk'),
128
129     # python
130     ('python-dev', 'python-devel'),
131     ('python-dbg', ''),
132     ('python-iso8601', ''),
133     ('python-gpg', 'python2-gpg'),  # defaults to ubuntu/fedora latest
134     ('python-crypto', 'python-crypto'),
135     ('python-markdown', 'python-markdown'),
136     ('python-dnspython', 'python-dns'),
137     ('python-pexpect', ''),  # for wintest only
138
139     ('python3', 'python3'),
140     ('python3-dev', 'python3-devel'),
141     ('python3-dbg', ''),
142     ('python3-iso8601', ''),
143     ('python3-gpg', 'python3-gpg'),  # defaults to ubuntu/fedora latest
144     ('python3-crypto', 'python3-crypto'),
145     ('python3-markdown', 'python3-markdown'),
146     ('python3-matplotlib', ''),
147     ('python3-dnspython', 'python3-dns'),
148     ('python3-pexpect', ''),  # for wintest only
149
150     ('', 'libsemanage-python'),
151     ('', 'policycoreutils-python'),
152
153     # perl
154     ('libparse-yapp-perl', 'perl-Parse-Yapp'),
155     ('libjson-perl', 'perl-JSON-Parse'),
156     ('perl-modules', ''),
157     ('', 'perl-Archive-Tar'),
158     ('', 'perl-ExtUtils-MakeMaker'),
159     ('', 'perl-Test-Base'),
160     ('', 'perl-generators'),
161     ('', 'perl-interpreter'),
162
163     # fs
164     ('xfslibs-dev', 'xfsprogs-devel'), # for xfs quota support
165     ('', 'glusterfs-api-devel'),
166     ('glusterfs-common', 'glusterfs-devel'),
167     ('libcephfs-dev', 'libcephfs-devel'),
168
169     # misc
170     # @ means group for rpm, use fedora as rpm default
171     ('build-essential', '@development-tools'),
172     ('debhelper', ''),
173     # rpm has no pkg for docbook-xml
174     ('docbook-xml', 'docbook-dtds'),
175     ('docbook-xsl', 'docbook-style-xsl'),
176     ('', 'keyutils-libs-devel'),
177     ('', 'which'),
178 ]
179
180
181 DEB_PKGS = COMMON + [pkg for pkg, _ in PKGS if pkg]
182 RPM_PKGS = COMMON + [pkg for _, pkg in PKGS if pkg]
183
184 GENERATED_MARKER = r"""
185 #
186 # This file is generated by 'bootstrap/template.py --render'
187 # See also bootstrap/config.py
188 #
189 """
190
191
192 APT_BOOTSTRAP = r"""
193 #!/bin/bash
194 {GENERATED_MARKER}
195 set -xueo pipefail
196
197 export DEBIAN_FRONTEND=noninteractive
198 apt-get -y update
199
200 apt-get -y install \
201     {pkgs}
202
203 apt-get -y autoremove
204 apt-get -y autoclean
205 apt-get -y clean
206 """
207
208
209 YUM_BOOTSTRAP = r"""
210 #!/bin/bash
211 {GENERATED_MARKER}
212 set -xueo pipefail
213
214 yum update -y
215 yum install -y epel-release
216 yum update -y
217
218 yum install -y \
219     {pkgs}
220
221 yum clean all
222
223 if [ ! -f /usr/bin/python3 ]; then
224     ln -sf /usr/bin/python3.6 /usr/bin/python3
225 fi
226 """
227
228
229 DNF_BOOTSTRAP = r"""
230 #!/bin/bash
231 {GENERATED_MARKER}
232 set -xueo pipefail
233
234 dnf update -y
235
236 dnf install -y \
237     --setopt=install_weak_deps=False \
238     {pkgs}
239
240 dnf clean all
241 """
242
243 ZYPPER_BOOTSTRAP = r"""
244 #!/bin/bash
245 {GENERATED_MARKER}
246 set -xueo pipefail
247
248 zypper --non-interactive refresh
249 zypper --non-interactive update
250 zypper --non-interactive install \
251     --no-recommends \
252     system-user-nobody \
253     {pkgs}
254
255 zypper --non-interactive clean
256
257 if [ -f /usr/lib/mit/bin/krb5-config ]; then
258     ln -sf /usr/lib/mit/bin/krb5-config /usr/bin/krb5-config
259 fi
260 """
261
262 # A generic shell script to setup locale
263 LOCALE_SETUP = r"""
264 #!/bin/bash
265 {GENERATED_MARKER}
266 set -xueo pipefail
267
268 # refer to /usr/share/i18n/locales
269 INPUTFILE=en_US
270 # refer to /usr/share/i18n/charmaps
271 CHARMAP=UTF-8
272 # locale to generate in /usr/lib/locale
273 # glibc/localedef will normalize UTF-8 to utf8, follow the naming style
274 LOCALE=$INPUTFILE.utf8
275
276 # if locale is already correct, exit
277 ( locale | grep LC_ALL | grep -i $LOCALE ) && exit 0
278
279 # if locale not available, generate locale into /usr/lib/locale
280 if ! ( locale --all-locales | grep -i $LOCALE )
281 then
282     # no-archive means create its own dir
283     localedef --inputfile $INPUTFILE --charmap $CHARMAP --no-archive $LOCALE
284 fi
285
286 # update locale conf and global env file
287 # set both LC_ALL and LANG for safe
288
289 # update conf for Debian family
290 FILE=/etc/default/locale
291 if [ -f $FILE ]
292 then
293     echo LC_ALL="$LOCALE" > $FILE
294     echo LANG="$LOCALE" >> $FILE
295 fi
296
297 # update conf for RedHat family
298 FILE=/etc/locale.conf
299 if [ -f $FILE ]
300 then
301     # LC_ALL is not valid in this file, set LANG only
302     echo LANG="$LOCALE" > $FILE
303 fi
304
305 # update global env file
306 FILE=/etc/environment
307 if [ -f $FILE ]
308 then
309     # append LC_ALL if not exist
310     grep LC_ALL $FILE || echo LC_ALL="$LOCALE" >> $FILE
311     # append LANG if not exist
312     grep LANG $FILE || echo LANG="$LOCALE" >> $FILE
313 fi
314 """
315
316
317 DOCKERFILE = r"""
318 {GENERATED_MARKER}
319 FROM {docker_image}
320
321 # pass in with --build-arg while build
322 ARG SHA1SUM
323 RUN [ -n $SHA1SUM ] && echo $SHA1SUM > /sha1sum.txt
324
325 ADD *.sh /tmp/
326 # need root permission, do it before USER samba
327 RUN /tmp/bootstrap.sh && /tmp/locale.sh
328
329 # if ld.gold exists, force link it to ld
330 RUN set -x; LD=$(which ld); LD_GOLD=$(which ld.gold); test -x $LD_GOLD && ln -sf $LD_GOLD $LD && test -x $LD && echo "$LD is now $LD_GOLD"
331
332 # make test can not work with root, so we have to create a new user
333 RUN useradd -m -U -s /bin/bash samba && \
334     mkdir -p /etc/sudoers.d && \
335     echo "samba ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/samba
336
337 USER samba
338 WORKDIR /home/samba
339 # samba tests rely on this
340 ENV USER=samba LC_ALL=en_US.utf8 LANG=en_US.utf8
341 """
342
343 # Vagrantfile snippet for each dist
344 VAGRANTFILE_SNIPPET = r"""
345     config.vm.define "{name}" do |v|
346         v.vm.box = "{vagrant_box}"
347         v.vm.hostname = "{name}"
348         v.vm.provision :shell, path: "{name}/bootstrap.sh"
349         v.vm.provision :shell, path: "{name}/locale.sh"
350     end
351 """
352
353 # global Vagrantfile with snippets for all dists
354 VAGRANTFILE_GLOBAL = r"""
355 {GENERATED_MARKER}
356
357 Vagrant.configure("2") do |config|
358     config.ssh.insert_key = false
359
360 {vagrantfile_snippets}
361
362 end
363 """
364
365
366 DEB_DISTS = {
367     'debian7': {
368         'docker_image': 'debian:7',
369         'vagrant_box': 'debian/wheezy64',
370         'replace': {
371             'libgnutls28-dev': 'libgnutls-dev',
372             'libsystemd-dev': '',  # not available, remove
373             'lmdb-utils': '',  # not available, remove
374             'liblmdb-dev': '',  # not available, remove
375             'python-gpg': 'python-gpgme',
376             'python3-gpg': '',  # no python3 gpg pkg available, remove
377             'language-pack-en': '',   # included in locales
378         }
379     },
380     'debian8': {
381         'docker_image': 'debian:8',
382         'vagrant_box': 'debian/jessie64',
383         'replace': {
384             'python-gpg': 'python-gpgme',
385             'python3-gpg': 'python3-gpgme',
386             'language-pack-en': '',   # included in locales
387         }
388     },
389     'debian9': {
390         'docker_image': 'debian:9',
391         'vagrant_box': 'debian/stretch64',
392         'replace': {
393             'language-pack-en': '',   # included in locales
394         }
395     },
396     'ubuntu1404': {
397         'docker_image': 'ubuntu:14.04',
398         'vagrant_box': 'ubuntu/trusty64',
399         'replace': {
400             'libsystemd-dev': '',  # remove
401             'libgnutls28-dev': 'libgnutls-dev',
402             'python-gpg': 'python-gpgme',
403             'python3-gpg': 'python3-gpgme',
404             'lmdb-utils': 'lmdb-utils/trusty-backports',
405             'liblmdb-dev': 'liblmdb-dev/trusty-backports',
406             'libunwind-dev': 'libunwind8-dev',
407             'glusterfs-common': '',
408             'libcephfs-dev': '',
409         }
410     },
411     'ubuntu1604': {
412         'docker_image': 'ubuntu:16.04',
413         'vagrant_box': 'ubuntu/xenial64',
414         'replace': {
415             'python-gpg': 'python-gpgme',
416             'python3-gpg': 'python3-gpgme',
417             'glusterfs-common': '',
418             'libcephfs-dev': '',
419         }
420     },
421     'ubuntu1804': {
422         'docker_image': 'ubuntu:18.04',
423         'vagrant_box': 'ubuntu/bionic64',
424     },
425 }
426
427
428 RPM_DISTS = {
429     'centos6': {
430         'docker_image': 'centos:6',
431         'vagrant_box': 'centos/6',
432         'bootstrap': YUM_BOOTSTRAP,
433         'replace': {
434             'lsb-release': 'redhat-lsb',
435             'python3': 'python36',
436             'python3-devel': 'python36-devel',
437             'python2-gpg': 'pygpgme',
438             'python3-gpg': '',  # no python3-gpg yet
439             '@development-tools': '"@Development Tools"',  # add quotes
440             'glibc-langpack-en': '',  # included in glibc-common
441             'glibc-locale-source': '',  # included in glibc-common
442             'procps-ng': 'procps',  # centos6 still use old name
443             # update perl core modules on centos
444             # fix: Can't locate Archive/Tar.pm in @INC
445             'perl': 'perl-core',
446             'rpcsvc-proto-devel': '',
447             'glusterfs-api-devel': '',
448             'glusterfs-devel': '',
449             'libcephfs-devel': '',
450         }
451     },
452     'centos7': {
453         'docker_image': 'centos:7',
454         'vagrant_box': 'centos/7',
455         'bootstrap': YUM_BOOTSTRAP,
456         'replace': {
457             'lsb-release': 'redhat-lsb',
458             'python3': 'python36',
459             'python3-crypto': 'python36-crypto',
460             'python3-devel': 'python36-devel',
461             'python3-dns': 'python36-dns',
462             'python3-gpg': 'python36-gpg',
463             'python3-iso8601' : 'python36-iso8601',
464             'python3-markdown': 'python36-markdown',
465             # although python36-devel is available
466             # after epel-release installed
467             # however, all other python3 pkgs are still python36-ish
468             'python2-gpg': 'pygpgme',
469             'python3-gpg': '',  # no python3-gpg yet
470             '@development-tools': '"@Development Tools"',  # add quotes
471             'glibc-langpack-en': '',  # included in glibc-common
472             'glibc-locale-source': '',  # included in glibc-common
473             # update perl core modules on centos
474             # fix: Can't locate Archive/Tar.pm in @INC
475             'perl': 'perl-core',
476             'rpcsvc-proto-devel': '',
477             'glusterfs-api-devel': '',
478             'glusterfs-devel': '',
479             'libcephfs-devel': '',
480         }
481     },
482     'fedora28': {
483         'docker_image': 'fedora:28',
484         'vagrant_box': 'fedora/28-cloud-base',
485         'bootstrap': DNF_BOOTSTRAP,
486         'replace': {
487             'lsb-release': 'redhat-lsb',
488         }
489     },
490     'fedora29': {
491         'docker_image': 'fedora:29',
492         'vagrant_box': 'fedora/29-cloud-base',
493         'bootstrap': DNF_BOOTSTRAP,
494         'replace': {
495             'lsb-release': 'redhat-lsb',
496         }
497     },
498     'opensuse150': {
499         'docker_image': 'opensuse/leap:15.0',
500         'vagrant_box': 'opensuse/openSUSE-15.0-x86_64',
501         'bootstrap': ZYPPER_BOOTSTRAP,
502         'replace': {
503             '@development-tools': '',
504             'dbus-devel': 'dbus-1-devel',
505             'docbook-style-xsl': 'docbook-xsl-stylesheets',
506             'glibc-common': 'glibc-locale',
507             'glibc-locale-source': 'glibc-i18ndata',
508             'glibc-langpack-en': '',
509             'jansson-devel': 'libjansson-devel',
510             'keyutils-libs-devel': 'keyutils-devel',
511             'krb5-workstation': 'krb5-client',
512             'libnsl2-devel': 'libnsl-devel',
513             'libsemanage-python': 'python2-semanage',
514             'nettle-devel': 'libnettle-devel',
515             'openldap-devel': 'openldap2-devel',
516             'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
517             'perl-JSON-Parse': 'perl-JSON-XS',
518             'perl-generators': '',
519             'perl-interpreter': '',
520             'procps-ng': 'procps',
521             'python-dns': 'python2-dnspython',
522             'python3-crypto': 'python3-pycrypto',
523             'python3-dns': 'python3-dnspython',
524             'python3-markdown': 'python3-Markdown',
525             'quota-devel': '',
526             'glusterfs-api-devel': '',
527         }
528     }
529 }
530
531
532 DEB_FAMILY = {
533     'name': 'deb',
534     'pkgs': DEB_PKGS,
535     'bootstrap': APT_BOOTSTRAP,  # family default
536     'dists': DEB_DISTS,
537 }
538
539
540 RPM_FAMILY = {
541     'name': 'rpm',
542     'pkgs': RPM_PKGS,
543     'bootstrap': YUM_BOOTSTRAP,  # family default
544     'dists': RPM_DISTS,
545 }
546
547
548 YML_HEADER = r"""
549 ---
550 packages:
551 """
552
553
554 def expand_family_dists(family):
555     dists = {}
556     for name, config in family['dists'].items():
557         config = config.copy()
558         config['name'] = name
559         config['home'] = join(OUT, name)
560         config['family'] = family['name']
561         config['GENERATED_MARKER'] = GENERATED_MARKER
562
563         # replace dist specific pkgs
564         replace = config.get('replace', {})
565         pkgs = []
566         for pkg in family['pkgs']:
567             pkg = replace.get(pkg, pkg)  # replace if exists or get self
568             if pkg:
569                 pkgs.append(pkg)
570         pkgs.sort()
571
572         lines = ['  - {}'.format(pkg) for pkg in pkgs]
573         config['packages.yml'] = YML_HEADER.lstrip() + os.linesep.join(lines)
574
575         sep = ' \\' + os.linesep + '    '
576         config['pkgs'] = sep.join(pkgs)
577
578         # get dist bootstrap template or fall back to family default
579         bootstrap_template = config.get('bootstrap', family['bootstrap'])
580         config['bootstrap.sh'] = bootstrap_template.format(**config).strip()
581         config['locale.sh'] = LOCALE_SETUP.format(**config).strip()
582
583         config['Dockerfile'] = DOCKERFILE.format(**config).strip()
584         # keep the indent, no strip
585         config['vagrantfile_snippet'] = VAGRANTFILE_SNIPPET.format(**config)
586
587         dists[name] = config
588     return dists
589
590
591 # expanded config for dists
592 DEB_DISTS_EXP = expand_family_dists(DEB_FAMILY)
593 RPM_DISTS_EXP = expand_family_dists(RPM_FAMILY)
594
595 # assemble all together
596 DISTS = {}
597 DISTS.update(DEB_DISTS_EXP)
598 DISTS.update(RPM_DISTS_EXP)
599
600
601 def render_vagrantfile(dists):
602     """
603     Render all snippets for each dist into global Vagrantfile.
604
605     Vagrant supports multiple vms in one Vagrantfile.
606     This make it easier to manage the fleet, e.g:
607
608     start all: vagrant up
609     start one: vagrant up ubuntu1804
610
611     All other commands apply to above syntax, e.g.: status, destroy, provision
612     """
613     # sort dists by name and put all vagrantfile snippets together
614     snippets = [
615         dists[dist]['vagrantfile_snippet']
616         for dist in sorted(dists.keys())]
617
618     return VAGRANTFILE_GLOBAL.format(
619             vagrantfile_snippets=''.join(snippets),
620             GENERATED_MARKER=GENERATED_MARKER
621             )
622
623
624 VAGRANTFILE = render_vagrantfile(DISTS)
625
626
627 # data we need to expose
628 __all__ = ['DISTS', 'VAGRANTFILE', 'OUT']