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