Spelling fixes s/whitch/which/
[Samba.git] / bootstrap / config.py
blobf4acdf5d92550496cfda3199230eb12b53f2956d
1 #!/usr/bin/env python3
3 # Copyright (C) Catalyst.Net Ltd 2019
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.
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.
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/>.
18 """
19 Manage dependencies and bootstrap environments for Samba.
21 Config file for packages and templates.
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')
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',
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 ('mingw-w64', 'mingw64-gcc'),
69 ('nettle-dev', 'nettle-devel'),
70 ('zlib1g-dev', 'zlib-devel'),
71 ('libbsd-dev', 'libbsd-devel'),
72 ('libaio-dev', 'libaio-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'),
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'),
106 # NAME1, NAME2
107 # for debian, locales provide locale support with language packs
108 # ubuntu split language packs to language-pack-xx
109 # for centos, glibc-common provide locale support with language packs
110 # fedora split language packs to glibc-langpack-xx
111 ('locales', 'glibc-common'), # required for locale
112 ('language-pack-en', 'glibc-langpack-en'), # we need en_US.UTF-8
113 ('bind9utils', 'bind-utils'),
114 ('dnsutils', ''),
115 ('xsltproc', 'libxslt'),
116 ('krb5-user', ''),
117 ('krb5-config', ''),
118 ('krb5-kdc', 'krb5-server'),
119 ('apt-utils', 'yum-utils'),
120 ('pkg-config', 'pkgconfig'),
121 ('procps', 'procps-ng'), # required for the free cmd in tests
122 ('lsb-release', 'lsb-release'), # we need lsb_relase to show info
123 ('', 'rpcgen'), # required for test
124 # refer: https://fedoraproject.org/wiki/Changes/SunRPCRemoval
125 ('', 'libtirpc-devel'), # for <rpc/rpc.h> header on fedora
126 ('', 'libnsl2-devel'), # for <rpcsvc/yp_prot.h> header on fedora
127 ('', 'rpcsvc-proto-devel'), # for <rpcsvc/rquota.h> header
128 ('mawk', 'gawk'),
130 # python
131 ('python-dev', 'python-devel'),
132 ('python-dbg', ''),
133 ('python-iso8601', ''),
134 ('python-gpg', 'python2-gpg'), # defaults to ubuntu/fedora latest
135 ('python-crypto', 'python-crypto'),
136 ('python-markdown', 'python-markdown'),
137 ('python-dnspython', 'python-dns'),
138 ('python-pexpect', ''), # for wintest only
140 ('python3', 'python3'),
141 ('python3-dev', 'python3-devel'),
142 ('python3-dbg', ''),
143 ('python3-iso8601', ''),
144 ('python3-gpg', 'python3-gpg'), # defaults to ubuntu/fedora latest
145 ('python3-crypto', 'python3-crypto'),
146 ('python3-markdown', 'python3-markdown'),
147 ('python3-matplotlib', ''),
148 ('python3-dnspython', 'python3-dns'),
149 ('python3-pexpect', ''), # for wintest only
151 ('', 'libsemanage-python'),
152 ('', 'policycoreutils-python'),
154 # perl
155 ('libparse-yapp-perl', 'perl-Parse-Yapp'),
156 ('libjson-perl', 'perl-JSON-Parse'),
157 ('perl-modules', ''),
158 ('', 'perl-Archive-Tar'),
159 ('', 'perl-ExtUtils-MakeMaker'),
160 ('', 'perl-Test-Base'),
161 ('', 'perl-generators'),
162 ('', 'perl-interpreter'),
164 # fs
165 ('xfslibs-dev', 'xfsprogs-devel'), # for xfs quota support
166 ('', 'glusterfs-api-devel'),
167 ('glusterfs-common', 'glusterfs-devel'),
168 ('libcephfs-dev', 'libcephfs-devel'),
170 # misc
171 # @ means group for rpm, use fedora as rpm default
172 ('build-essential', '@development-tools'),
173 ('debhelper', ''),
174 # rpm has no pkg for docbook-xml
175 ('docbook-xml', 'docbook-dtds'),
176 ('docbook-xsl', 'docbook-style-xsl'),
177 ('', 'keyutils-libs-devel'),
178 ('', 'which'),
182 DEB_PKGS = COMMON + [pkg for pkg, _ in PKGS if pkg]
183 RPM_PKGS = COMMON + [pkg for _, pkg in PKGS if pkg]
185 GENERATED_MARKER = r"""
187 # This file is generated by 'bootstrap/template.py --render'
188 # See also bootstrap/config.py
193 APT_BOOTSTRAP = r"""
194 #!/bin/bash
195 {GENERATED_MARKER}
196 set -xueo pipefail
198 export DEBIAN_FRONTEND=noninteractive
199 apt-get -y update
201 apt-get -y install \
202 {pkgs}
204 apt-get -y autoremove
205 apt-get -y autoclean
206 apt-get -y clean
210 YUM_BOOTSTRAP = r"""
211 #!/bin/bash
212 {GENERATED_MARKER}
213 set -xueo pipefail
215 yum update -y
216 yum install -y epel-release
217 yum install -y yum-plugin-copr
218 yum copr enable -y sergiomb/SambaAD
219 yum update -y
221 yum install -y \
222 {pkgs}
224 yum clean all
226 if [ ! -f /usr/bin/python3 ]; then
227 ln -sf /usr/bin/python3.6 /usr/bin/python3
232 DNF_BOOTSTRAP = r"""
233 #!/bin/bash
234 {GENERATED_MARKER}
235 set -xueo pipefail
237 dnf update -y
239 dnf install -y \
240 --setopt=install_weak_deps=False \
241 {pkgs}
243 dnf clean all
246 ZYPPER_BOOTSTRAP = r"""
247 #!/bin/bash
248 {GENERATED_MARKER}
249 set -xueo pipefail
251 zypper --non-interactive refresh
252 zypper --non-interactive update
253 zypper --non-interactive install \
254 --no-recommends \
255 system-user-nobody \
256 {pkgs}
258 zypper --non-interactive clean
260 if [ -f /usr/lib/mit/bin/krb5-config ]; then
261 ln -sf /usr/lib/mit/bin/krb5-config /usr/bin/krb5-config
265 # A generic shell script to setup locale
266 LOCALE_SETUP = r"""
267 #!/bin/bash
268 {GENERATED_MARKER}
269 set -xueo pipefail
271 # refer to /usr/share/i18n/locales
272 INPUTFILE=en_US
273 # refer to /usr/share/i18n/charmaps
274 CHARMAP=UTF-8
275 # locale to generate in /usr/lib/locale
276 # glibc/localedef will normalize UTF-8 to utf8, follow the naming style
277 LOCALE=$INPUTFILE.utf8
279 # if locale is already correct, exit
280 ( locale | grep LC_ALL | grep -i $LOCALE ) && exit 0
282 # if locale not available, generate locale into /usr/lib/locale
283 if ! ( locale --all-locales | grep -i $LOCALE )
284 then
285 # no-archive means create its own dir
286 localedef --inputfile $INPUTFILE --charmap $CHARMAP --no-archive $LOCALE
289 # update locale conf and global env file
290 # set both LC_ALL and LANG for safe
292 # update conf for Debian family
293 FILE=/etc/default/locale
294 if [ -f $FILE ]
295 then
296 echo LC_ALL="$LOCALE" > $FILE
297 echo LANG="$LOCALE" >> $FILE
300 # update conf for RedHat family
301 FILE=/etc/locale.conf
302 if [ -f $FILE ]
303 then
304 # LC_ALL is not valid in this file, set LANG only
305 echo LANG="$LOCALE" > $FILE
308 # update global env file
309 FILE=/etc/environment
310 if [ -f $FILE ]
311 then
312 # append LC_ALL if not exist
313 grep LC_ALL $FILE || echo LC_ALL="$LOCALE" >> $FILE
314 # append LANG if not exist
315 grep LANG $FILE || echo LANG="$LOCALE" >> $FILE
320 DOCKERFILE = r"""
321 {GENERATED_MARKER}
322 FROM {docker_image}
324 # pass in with --build-arg while build
325 ARG SHA1SUM
326 RUN [ -n $SHA1SUM ] && echo $SHA1SUM > /sha1sum.txt
328 ADD *.sh /tmp/
329 # need root permission, do it before USER samba
330 RUN /tmp/bootstrap.sh && /tmp/locale.sh
332 # if ld.gold exists, force link it to ld
333 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"
335 # make test can not work with root, so we have to create a new user
336 RUN useradd -m -U -s /bin/bash samba && \
337 mkdir -p /etc/sudoers.d && \
338 echo "samba ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/samba
340 USER samba
341 WORKDIR /home/samba
342 # samba tests rely on this
343 ENV USER=samba LC_ALL=en_US.utf8 LANG=en_US.utf8
346 # Vagrantfile snippet for each dist
347 VAGRANTFILE_SNIPPET = r"""
348 config.vm.define "{name}" do |v|
349 v.vm.box = "{vagrant_box}"
350 v.vm.hostname = "{name}"
351 v.vm.provision :shell, path: "{name}/bootstrap.sh"
352 v.vm.provision :shell, path: "{name}/locale.sh"
356 # global Vagrantfile with snippets for all dists
357 VAGRANTFILE_GLOBAL = r"""
358 {GENERATED_MARKER}
360 Vagrant.configure("2") do |config|
361 config.ssh.insert_key = false
363 {vagrantfile_snippets}
369 DEB_DISTS = {
370 'debian7': {
371 'docker_image': 'debian:7',
372 'vagrant_box': 'debian/wheezy64',
373 'replace': {
374 'libgnutls28-dev': 'libgnutls-dev',
375 'libsystemd-dev': '', # not available, remove
376 'lmdb-utils': '', # not available, remove
377 'liblmdb-dev': '', # not available, remove
378 'python-gpg': 'python-gpgme',
379 'python3-gpg': '', # no python3 gpg pkg available, remove
380 'language-pack-en': '', # included in locales
383 'debian8': {
384 'docker_image': 'debian:8',
385 'vagrant_box': 'debian/jessie64',
386 'replace': {
387 'python-gpg': 'python-gpgme',
388 'python3-gpg': 'python3-gpgme',
389 'language-pack-en': '', # included in locales
392 'debian9': {
393 'docker_image': 'debian:9',
394 'vagrant_box': 'debian/stretch64',
395 'replace': {
396 'language-pack-en': '', # included in locales
399 'ubuntu1404': {
400 'docker_image': 'ubuntu:14.04',
401 'vagrant_box': 'ubuntu/trusty64',
402 'replace': {
403 'libsystemd-dev': '', # remove
404 'libgnutls28-dev': 'libgnutls-dev',
405 'python-gpg': 'python-gpgme',
406 'python3-gpg': 'python3-gpgme',
407 'lmdb-utils': 'lmdb-utils/trusty-backports',
408 'liblmdb-dev': 'liblmdb-dev/trusty-backports',
409 'libunwind-dev': 'libunwind8-dev',
410 'glusterfs-common': '',
411 'libcephfs-dev': '',
414 'ubuntu1604': {
415 'docker_image': 'ubuntu:16.04',
416 'vagrant_box': 'ubuntu/xenial64',
417 'replace': {
418 'python-gpg': 'python-gpgme',
419 'python3-gpg': 'python3-gpgme',
420 'glusterfs-common': '',
421 'libcephfs-dev': '',
424 'ubuntu1804': {
425 'docker_image': 'ubuntu:18.04',
426 'vagrant_box': 'ubuntu/bionic64',
431 RPM_DISTS = {
432 'centos6': {
433 'docker_image': 'centos:6',
434 'vagrant_box': 'centos/6',
435 'bootstrap': YUM_BOOTSTRAP,
436 'replace': {
437 'lsb-release': 'redhat-lsb',
438 'python3': 'python36',
439 'python3-devel': 'python36-devel',
440 'python2-gpg': 'pygpgme',
441 'python3-gpg': '', # no python3-gpg yet
442 '@development-tools': '"@Development Tools"', # add quotes
443 'glibc-langpack-en': '', # included in glibc-common
444 'glibc-locale-source': '', # included in glibc-common
445 'procps-ng': 'procps', # centos6 still use old name
446 # update perl core modules on centos
447 # fix: Can't locate Archive/Tar.pm in @INC
448 'perl': 'perl-core',
449 'rpcsvc-proto-devel': '',
450 'glusterfs-api-devel': '',
451 'glusterfs-devel': '',
452 'libcephfs-devel': '',
455 'centos7': {
456 'docker_image': 'centos:7',
457 'vagrant_box': 'centos/7',
458 'bootstrap': YUM_BOOTSTRAP,
459 'replace': {
460 'lsb-release': 'redhat-lsb',
461 'python3': 'python36',
462 'python3-crypto': 'python36-crypto',
463 'python3-devel': 'python36-devel',
464 'python3-dns': 'python36-dns',
465 'python3-gpg': 'python36-gpg',
466 'python3-iso8601' : 'python36-iso8601',
467 'python3-markdown': 'python36-markdown',
468 # although python36-devel is available
469 # after epel-release installed
470 # however, all other python3 pkgs are still python36-ish
471 'python2-gpg': 'pygpgme',
472 'python3-gpg': '', # no python3-gpg yet
473 '@development-tools': '"@Development Tools"', # add quotes
474 'glibc-langpack-en': '', # included in glibc-common
475 'glibc-locale-source': '', # included in glibc-common
476 # update perl core modules on centos
477 # fix: Can't locate Archive/Tar.pm in @INC
478 'perl': 'perl-core',
479 'rpcsvc-proto-devel': '',
480 'glusterfs-api-devel': '',
481 'glusterfs-devel': '',
482 'libcephfs-devel': '',
483 'gnutls-devel': 'compat-gnutls34-devel',
486 'fedora28': {
487 'docker_image': 'fedora:28',
488 'vagrant_box': 'fedora/28-cloud-base',
489 'bootstrap': DNF_BOOTSTRAP,
490 'replace': {
491 'lsb-release': 'redhat-lsb',
494 'fedora29': {
495 'docker_image': 'fedora:29',
496 'vagrant_box': 'fedora/29-cloud-base',
497 'bootstrap': DNF_BOOTSTRAP,
498 'replace': {
499 'lsb-release': 'redhat-lsb',
502 'fedora30': {
503 'docker_image': 'fedora:30',
504 'vagrant_box': 'fedora/30-cloud-base',
505 'bootstrap': DNF_BOOTSTRAP,
506 'replace': {
507 'lsb-release': 'redhat-lsb',
510 'opensuse150': {
511 'docker_image': 'opensuse/leap:15.0',
512 'vagrant_box': 'opensuse/openSUSE-15.0-x86_64',
513 'bootstrap': ZYPPER_BOOTSTRAP,
514 'replace': {
515 '@development-tools': '',
516 'dbus-devel': 'dbus-1-devel',
517 'docbook-style-xsl': 'docbook-xsl-stylesheets',
518 'glibc-common': 'glibc-locale',
519 'glibc-locale-source': 'glibc-i18ndata',
520 'glibc-langpack-en': '',
521 'jansson-devel': 'libjansson-devel',
522 'keyutils-libs-devel': 'keyutils-devel',
523 'krb5-workstation': 'krb5-client',
524 'libnsl2-devel': 'libnsl-devel',
525 'libsemanage-python': 'python2-semanage',
526 'nettle-devel': 'libnettle-devel',
527 'openldap-devel': 'openldap2-devel',
528 'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
529 'perl-JSON-Parse': 'perl-JSON-XS',
530 'perl-generators': '',
531 'perl-interpreter': '',
532 'procps-ng': 'procps',
533 'python-dns': 'python2-dnspython',
534 'python3-crypto': 'python3-pycrypto',
535 'python3-dns': 'python3-dnspython',
536 'python3-markdown': 'python3-Markdown',
537 'quota-devel': '',
538 'glusterfs-api-devel': '',
539 'libtasn1-tools': '', # asn1Parser is part of libtasn1
540 'mingw64-gcc': '', # doesn't exist
543 'opensuse151': {
544 'docker_image': 'opensuse/leap:15.1',
545 'vagrant_box': 'opensuse/openSUSE-15.1-x86_64',
546 'bootstrap': ZYPPER_BOOTSTRAP,
547 'replace': {
548 '@development-tools': '',
549 'dbus-devel': 'dbus-1-devel',
550 'docbook-style-xsl': 'docbook-xsl-stylesheets',
551 'glibc-common': 'glibc-locale',
552 'glibc-locale-source': 'glibc-i18ndata',
553 'glibc-langpack-en': '',
554 'jansson-devel': 'libjansson-devel',
555 'keyutils-libs-devel': 'keyutils-devel',
556 'krb5-workstation': 'krb5-client',
557 'libnsl2-devel': 'libnsl-devel',
558 'libsemanage-python': 'python2-semanage',
559 'nettle-devel': 'libnettle-devel',
560 'openldap-devel': 'openldap2-devel',
561 'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
562 'perl-JSON-Parse': 'perl-JSON-XS',
563 'perl-generators': '',
564 'perl-interpreter': '',
565 'procps-ng': 'procps',
566 'python-dns': 'python2-dnspython',
567 'python3-crypto': 'python3-pycrypto',
568 'python3-dns': 'python3-dnspython',
569 'python3-markdown': 'python3-Markdown',
570 'quota-devel': '',
571 'glusterfs-api-devel': '',
572 'libtasn1-tools': '', # asn1Parser is part of libtasn1
573 'mingw64-gcc': '', # doesn't exist
579 DEB_FAMILY = {
580 'name': 'deb',
581 'pkgs': DEB_PKGS,
582 'bootstrap': APT_BOOTSTRAP, # family default
583 'dists': DEB_DISTS,
587 RPM_FAMILY = {
588 'name': 'rpm',
589 'pkgs': RPM_PKGS,
590 'bootstrap': YUM_BOOTSTRAP, # family default
591 'dists': RPM_DISTS,
595 YML_HEADER = r"""
597 packages:
601 def expand_family_dists(family):
602 dists = {}
603 for name, config in family['dists'].items():
604 config = config.copy()
605 config['name'] = name
606 config['home'] = join(OUT, name)
607 config['family'] = family['name']
608 config['GENERATED_MARKER'] = GENERATED_MARKER
610 # replace dist specific pkgs
611 replace = config.get('replace', {})
612 pkgs = []
613 for pkg in family['pkgs']:
614 pkg = replace.get(pkg, pkg) # replace if exists or get self
615 if pkg:
616 pkgs.append(pkg)
617 pkgs.sort()
619 lines = [' - {}'.format(pkg) for pkg in pkgs]
620 config['packages.yml'] = YML_HEADER.lstrip() + os.linesep.join(lines)
622 sep = ' \\' + os.linesep + ' '
623 config['pkgs'] = sep.join(pkgs)
625 # get dist bootstrap template or fall back to family default
626 bootstrap_template = config.get('bootstrap', family['bootstrap'])
627 config['bootstrap.sh'] = bootstrap_template.format(**config).strip()
628 config['locale.sh'] = LOCALE_SETUP.format(**config).strip()
630 config['Dockerfile'] = DOCKERFILE.format(**config).strip()
631 # keep the indent, no strip
632 config['vagrantfile_snippet'] = VAGRANTFILE_SNIPPET.format(**config)
634 dists[name] = config
635 return dists
638 # expanded config for dists
639 DEB_DISTS_EXP = expand_family_dists(DEB_FAMILY)
640 RPM_DISTS_EXP = expand_family_dists(RPM_FAMILY)
642 # assemble all together
643 DISTS = {}
644 DISTS.update(DEB_DISTS_EXP)
645 DISTS.update(RPM_DISTS_EXP)
648 def render_vagrantfile(dists):
650 Render all snippets for each dist into global Vagrantfile.
652 Vagrant supports multiple vms in one Vagrantfile.
653 This make it easier to manage the fleet, e.g:
655 start all: vagrant up
656 start one: vagrant up ubuntu1804
658 All other commands apply to above syntax, e.g.: status, destroy, provision
660 # sort dists by name and put all vagrantfile snippets together
661 snippets = [
662 dists[dist]['vagrantfile_snippet']
663 for dist in sorted(dists.keys())]
665 return VAGRANTFILE_GLOBAL.format(
666 vagrantfile_snippets=''.join(snippets),
667 GENERATED_MARKER=GENERATED_MARKER
671 VAGRANTFILE = render_vagrantfile(DISTS)
674 # data we need to expose
675 __all__ = ['DISTS', 'VAGRANTFILE', 'OUT']