bootstrap/config.py: adjust package list to align current ci image
[garming/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, '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     'gcc',
41     'gdb',
42     'git',
43     'htop',
44     'make',
45     'perl',
46     'psmisc',  # for pstree in test
47     'rng-tools',
48     'sed',
49     'sudo',  # docker images has no sudo by default
50     'tree',
51 ]
52
53
54 # define pkgs for all packaging systems in parallel
55 # make it easier to find missing ones
56 # use latest ubuntu and fedora as defaults
57 # deb, rpm, ...
58 PKGS = [
59     # NAME1-dev, NAME2-devel
60     ('lmdb-utils', 'lmdb-devel'),
61     ('nettle-dev', 'nettle-devel'),
62     ('zlib1g-dev', 'zlib-devel'),
63     ('libbsd-dev', 'libbsd-devel'),
64     ('libaio-dev', 'libaio-devel'),
65     ('libarchive-dev', 'libarchive-devel'),
66     ('libblkid-dev', 'libblkid-devel'),
67     ('libcap-dev', 'libcap-devel'),
68     ('libacl1-dev', 'libacl-devel'),
69     ('libattr1-dev', 'libattr-devel'),
70
71     # libNAME1-dev, NAME2-devel
72     ('libpopt-dev', 'popt-devel'),
73     ('libreadline-dev', 'readline-devel'),
74     ('libjansson-dev', 'jansson-devel'),
75     ('liblmdb-dev', 'lmdb-devel'),
76     ('libncurses5-dev', 'ncurses-devel'),
77     # NOTE: Debian 7+ or Ubuntu 16.04+
78     ('libsystemd-dev', 'systemd-devel'),
79     ('libkrb5-dev', 'krb5-devel'),
80     ('libldap2-dev', 'openldap-devel'),
81     ('libcups2-dev', 'cups-devel'),
82     ('libpam0g-dev', 'pam-devel'),
83     ('libgpgme11-dev', 'gpgme-devel'),
84     # NOTE: Debian 8+ and Ubuntu 14.04+
85     ('libgnutls28-dev', 'gnutls-devel'),
86     ('libtasn1-bin', ''),
87     ('libtasn1-dev', 'libtasn1-devel'),
88     ('', 'quota-devel'),
89     ('uuid-dev', 'libuuid-devel'),
90     ('libjs-jquery', ''),
91
92     # NAME1, NAME2
93     # for debian, locales provide locale support with language packs
94     # ubuntu split language packs to language-pack-xx
95     # for centos, glibc-common provide locale support with language packs
96     # fedora split language packs  to glibc-langpack-xx
97     ('locales', 'glibc-common'),  # required for locale
98     ('language-pack-en', 'glibc-langpack-en'),  # we need en_US.UTF-8
99     ('bind9utils', 'bind-utils'),
100     ('dnsutils', ''),
101     ('xsltproc', 'libxslt'),
102     ('krb5-user', ''),
103     ('krb5-config', ''),
104     ('', 'krb5-server'),
105     ('apt-utils', 'yum-utils'),
106     ('pkg-config', 'pkgconfig'),
107     ('procps', 'procps-ng'),  # required for the free cmd in tests
108     ('lsb-release', 'redhat-lsb'),  # we need lsb_relase to show info
109     ('', 'rpcgen'),  # required for test
110     # refer: https://fedoraproject.org/wiki/Changes/SunRPCRemoval
111     ('', 'libtirpc-devel'),  # for <rpc/rpc.h> header on fedora
112     ('', 'libnsl2-devel'),  # for <rpcsvc/yp_prot.h> header on fedora
113     ('mawk', 'gawk'),
114
115     # python
116     ('python-dev', 'python-devel'),
117     ('python-dbg', ''),
118     ('python-iso8601', ''),
119     ('python-gpg', 'python2-gpg'),  # defaults to ubuntu/fedora latest
120     ('python-crypto', 'python-crypto'),
121     ('python-markdown', 'python-markdown'),
122     ('python-dnspython', 'python-dns'),
123     ('python-pexpect', ''),  # for wintest only
124
125     ('python3-dev', 'python3-devel'),
126     ('python3-dbg', ''),
127     ('python3-iso8601', ''),
128     ('python3-gpg', 'python3-gpg'),  # defaults to ubuntu/fedora latest
129     ('python3-crypto', 'python3-crypto'),
130     ('python3-markdown', 'python3-markdown'),
131     ('python3-matplotlib', ''),
132     ('python3-dnspython', 'python3-dns'),
133     ('python3-pexpect', ''),  # for wintest only
134
135     ('', 'libsemanage-python'),
136     ('', 'policycoreutils-python'),
137
138     # perl
139     ('libparse-yapp-perl', 'perl-Parse-Yapp'),
140     ('libjson-perl', 'perl-JSON-Parse'),
141     ('perl-modules', ''),
142     ('', 'perl-Archive-Tar'),
143     ('', 'perl-ExtUtils-MakeMaker'),
144     ('', 'perl-Test-Base'),
145     ('', 'perl-generators'),
146     ('', 'perl-interpreter'),
147
148     # misc
149     # @ means group for rpm, use fedora as rpm default
150     ('build-essential', '@development-tools'),
151     ('debhelper', ''),
152     # rpm has no pkg for docbook-xml
153     ('docbook-xml', 'docbook-dtds'),
154     ('docbook-xsl', 'docbook-style-xsl'),
155     ('flex', ''),
156     ('', 'keyutils-libs-devel'),
157
158 ]
159
160
161 DEB_PKGS = COMMON + [pkg for pkg, _ in PKGS if pkg]
162 RPM_PKGS = COMMON + [pkg for _, pkg in PKGS if pkg]
163
164
165 APT_BOOTSTRAP = r"""
166 #!/bin/bash
167 set -xueo pipefail
168
169 export DEBIAN_FRONTEND=noninteractive
170 apt-get -y update
171
172 apt-get -y install \
173     {pkgs}
174
175 apt-get -y autoremove
176 apt-get -y autoclean
177 apt-get -y clean
178 """
179
180
181 YUM_BOOTSTRAP = r"""
182 #!/bin/bash
183 set -xueo pipefail
184
185 yum -y -q update
186 yum -y -q install epel-release
187 yum -y -q update
188
189 yum -y -q --verbose install \
190     {pkgs}
191
192 yum clean all
193 """
194
195
196 DNF_BOOTSTRAP = r"""
197 #!/bin/bash
198 set -xueo pipefail
199
200 dnf -y -q update
201
202 dnf -y -q --verbose install \
203     {pkgs}
204
205 dnf clean all
206 """
207
208
209 # A generic shell script to setup locale
210 LOCALE_SETUP = r"""
211 #!/bin/bash
212 set -xueo pipefail
213
214 # refer to /usr/share/i18n/locales
215 INPUTFILE=en_US
216 # refer to /usr/share/i18n/charmaps
217 CHARMAP=UTF-8
218 # locale to generate in /usr/lib/locale
219 # glibc/localedef will normalize UTF-8 to utf8, follow the naming style
220 LOCALE=$INPUTFILE.utf8
221
222 # if locale is already correct, exit
223 ( locale | grep LC_ALL | grep -i $LOCALE ) && exit 0
224
225 # if locale not available, generate locale into /usr/lib/locale
226 if ! ( locale --all-locales | grep -i $LOCALE )
227 then
228     # no-archive means create its own dir
229     localedef --inputfile $INPUTFILE --charmap $CHARMAP --no-archive $LOCALE
230 fi
231
232 # update locale conf and global env file
233 # set both LC_ALL and LANG for safe
234
235 # update conf for Debian family
236 FILE=/etc/default/locale
237 if [ -f $FILE ]
238 then
239     echo LC_ALL="$LOCALE" > $FILE
240     echo LANG="$LOCALE" >> $FILE
241 fi
242
243 # update conf for RedHat family
244 FILE=/etc/locale.conf
245 if [ -f $FILE ]
246 then
247     # LC_ALL is not valid in this file, set LANG only
248     echo LANG="$LOCALE" > $FILE
249 fi
250
251 # update global env file
252 FILE=/etc/environment
253 if [ -f $FILE ]
254 then
255     # append LC_ALL if not exist
256     grep LC_ALL $FILE || echo LC_ALL="$LOCALE" >> $FILE
257     # append LANG if not exist
258     grep LANG $FILE || echo LANG="$LOCALE" >> $FILE
259 fi
260 """
261
262
263 DOCKERFILE = r"""
264 FROM {docker_image}
265
266 # we will use this image to run ci, these ENV vars are important
267 ENV CC="ccache gcc"
268
269 ADD *.sh /tmp/
270 # need root permission, do it before USER samba
271 RUN /tmp/bootstrap.sh && /tmp/locale.sh
272
273 # make test can not work with root, so we have to create a new user
274 RUN useradd -m -s /bin/bash samba && \
275     mkdir -p /etc/sudoers.d && \
276     echo "samba ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/samba
277
278 USER samba
279 WORKDIR /home/samba
280 # samba tests rely on this
281 ENV USER=samba LC_ALL=en_US.utf8 LANG=en_US.utf8
282 """
283
284 # Vagrantfile snippet for each dist
285 VAGRANTFILE_SNIPPET = r"""
286     config.vm.define "{name}" do |v|
287         v.vm.box = "{vagrant_box}"
288         v.vm.hostname = "{name}"
289         v.vm.provision :shell, path: "{name}/bootstrap.sh"
290         v.vm.provision :shell, path: "{name}/locale.sh"
291     end
292 """
293
294 # global Vagrantfile with snippets for all dists
295 VAGRANTFILE_GLOBAL = r"""
296 Vagrant.configure("2") do |config|
297     config.ssh.insert_key = false
298
299 {vagrantfile_snippets}
300
301 end
302 """
303
304
305 DEB_DISTS = {
306     'debian7': {
307         'docker_image': 'debian:7',
308         'vagrant_box': 'debian/wheezy64',
309         'replace': {
310             'libgnutls28-dev': 'libgnutls-dev',
311             'libsystemd-dev': '',  # not available, remove
312             'lmdb-utils': '',  # not available, remove
313             'liblmdb-dev': '',  # not available, remove
314             'python-gpg': 'python-gpgme',
315             'python3-gpg': '',  # no python3 gpg pkg available, remove
316             'language-pack-en': '',   # included in locales
317         }
318     },
319     'debian8': {
320         'docker_image': 'debian:8',
321         'vagrant_box': 'debian/jessie64',
322         'replace': {
323             'python-gpg': 'python-gpgme',
324             'python3-gpg': 'python3-gpgme',
325             'language-pack-en': '',   # included in locales
326         }
327     },
328     'debian9': {
329         'docker_image': 'debian:9',
330         'vagrant_box': 'debian/stretch64',
331         'replace': {
332             'language-pack-en': '',   # included in locales
333         }
334     },
335     'ubuntu1404': {
336         'docker_image': 'ubuntu:14.04',
337         'vagrant_box': 'ubuntu/trusty64',
338         'replace': {
339             'libsystemd-dev': '',  # remove
340             'libgnutls28-dev': 'libgnutls-dev',
341             'python-gpg': 'python-gpgme',
342             'python3-gpg': 'python3-gpgme',
343             'lmdb-utils': 'lmdb-utils/trusty-backports',
344             'liblmdb-dev': 'liblmdb-dev/trusty-backports',
345         }
346     },
347     'ubuntu1604': {
348         'docker_image': 'ubuntu:16.04',
349         'vagrant_box': 'ubuntu/xenial64',
350         'replace': {
351             'python-gpg': 'python-gpgme',
352             'python3-gpg': 'python3-gpgme',
353         }
354     },
355     'ubuntu1804': {
356         'docker_image': 'ubuntu:18.04',
357         'vagrant_box': 'ubuntu/bionic64',
358     },
359 }
360
361
362 RPM_DISTS = {
363     'centos6': {
364         'docker_image': 'centos:6',
365         'vagrant_box': 'centos/6',
366         'bootstrap': YUM_BOOTSTRAP,
367         'replace': {
368             'python3-devel': 'python34-devel',
369             'python2-gpg': 'pygpgme',
370             'python3-gpg': '',  # no python3-gpg yet
371             '@development-tools': '"@Development Tools"',  # add quotes
372             'glibc-langpack-en': '',  # included in glibc-common
373             'glibc-locale-source': '',  # included in glibc-common
374             'procps-ng': 'procps',  # centos6 still use old name
375             # update perl core modules on centos
376             # fix: Can't locate Archive/Tar.pm in @INC
377             'perl': 'perl-core',
378         }
379     },
380     'centos7': {
381         'docker_image': 'centos:7',
382         'vagrant_box': 'centos/7',
383         'bootstrap': YUM_BOOTSTRAP,
384         'replace': {
385             'python3-devel': 'python34-devel',
386             # although python36-devel is available
387             # after epel-release installed
388             # however, all other python3 pkgs are still python34-ish
389             'python2-gpg': 'pygpgme',
390             'python3-gpg': '',  # no python3-gpg yet
391             '@development-tools': '"@Development Tools"',  # add quotes
392             'glibc-langpack-en': '',  # included in glibc-common
393             'glibc-locale-source': '',  # included in glibc-common
394             # update perl core modules on centos
395             # fix: Can't locate Archive/Tar.pm in @INC
396             'perl': 'perl-core',
397         }
398     },
399     'fedora28': {
400         'docker_image': 'fedora:28',
401         'vagrant_box': 'fedora/28-cloud-base',
402         'bootstrap': DNF_BOOTSTRAP,
403     },
404     'fedora29': {
405         'docker_image': 'fedora:29',
406         'vagrant_box': 'fedora/29-cloud-base',
407         'bootstrap': DNF_BOOTSTRAP,
408     },
409 }
410
411
412 DEB_FAMILY = {
413     'name': 'deb',
414     'pkgs': DEB_PKGS,
415     'bootstrap': APT_BOOTSTRAP,  # family default
416     'dists': DEB_DISTS,
417 }
418
419
420 RPM_FAMILY = {
421     'name': 'rpm',
422     'pkgs': RPM_PKGS,
423     'bootstrap': YUM_BOOTSTRAP,  # family default
424     'dists': RPM_DISTS,
425 }
426
427
428 YML_HEADER = r"""
429 ---
430 packages:
431 """
432
433
434 def expand_family_dists(family):
435     dists = {}
436     for name, config in family['dists'].items():
437         config = config.copy()
438         config['name'] = name
439         config['home'] = join(OUT, name)
440         config['family'] = family['name']
441
442         # replace dist specific pkgs
443         replace = config.get('replace', {})
444         pkgs = []
445         for pkg in family['pkgs']:
446             pkg = replace.get(pkg, pkg)  # replace if exists or get self
447             if pkg:
448                 pkgs.append(pkg)
449         pkgs.sort()
450
451         lines = ['  - {}'.format(pkg) for pkg in pkgs]
452         config['packages.yml'] = YML_HEADER.lstrip() + os.linesep.join(lines)
453
454         sep = ' \\' + os.linesep + '    '
455         config['pkgs'] = sep.join(pkgs)
456
457         # get dist bootstrap template or fall back to family default
458         bootstrap_template = config.get('bootstrap', family['bootstrap'])
459         config['bootstrap.sh'] = bootstrap_template.format(**config).strip()
460         config['locale.sh'] = LOCALE_SETUP.format(**config).strip()
461
462         config['Dockerfile'] = DOCKERFILE.format(**config).strip()
463         # keep the indent, no strip
464         config['vagrantfile_snippet'] = VAGRANTFILE_SNIPPET.format(**config)
465
466         dists[name] = config
467     return dists
468
469
470 # expanded config for dists
471 DEB_DISTS_EXP = expand_family_dists(DEB_FAMILY)
472 RPM_DISTS_EXP = expand_family_dists(RPM_FAMILY)
473
474 # assemble all together
475 DISTS = {}
476 DISTS.update(DEB_DISTS_EXP)
477 DISTS.update(RPM_DISTS_EXP)
478
479
480 def render_vagrantfile(dists):
481     """
482     Render all snippets for each dist into global Vagrantfile.
483
484     Vagrant supports multiple vms in one Vagrantfile.
485     This make it easier to manage the fleet, e.g:
486
487     start all: vagrant up
488     start one: vagrant up ubuntu1804
489
490     All other commands apply to above syntax, e.g.: status, destroy, provision
491     """
492     # sort dists by name and put all vagrantfile snippets together
493     snippets = [
494         dists[dist]['vagrantfile_snippet']
495         for dist in sorted(dists.keys())]
496
497     return VAGRANTFILE_GLOBAL.format(vagrantfile_snippets=''.join(snippets))
498
499
500 VAGRANTFILE = render_vagrantfile(DISTS)
501
502
503 # data we need to expose
504 __all__ = ['DISTS', 'VAGRANTFILE', 'OUT']