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