source: devel/pb-modules/lib/ProjectBuilder/VE.pm @ 2397

Last change on this file since 2397 was 2333, checked in by Bruno Cornec, 2 years ago

Initial support of apk for pb

File size: 16.0 KB
Line 
1#!/usr/bin/perl -w
2#
3# Common functions for virtual environment
4#
5# Copyright B. Cornec 2007-2016
6# Eric Anderson's changes are (c) Copyright 2012 Hewlett Packard
7# Provided under the GPL v2
8#
9# $Id$
10#
11
12package ProjectBuilder::VE;
13
14use strict;
15use Data::Dumper;
16use Carp 'confess';
17use English;
18use File::Basename;
19use ProjectBuilder::Version;
20use ProjectBuilder::Base;
21use ProjectBuilder::Conf;
22use ProjectBuilder::Distribution;
23
24# Global vars
25# Inherit from the "Exporter" module which handles exporting functions.
26 
27use vars qw($VERSION $REVISION @ISA @EXPORT);
28use Exporter;
29 
30# Export, by default, all the functions into the namespace of
31# any code which uses this module.
32 
33our @ISA = qw(Exporter);
34our @EXPORT = qw(pb_ve_launch pb_ve_snap pb_ve_get_type pb_ve_docker_repo pb_ve_docker_get_image);
35
36($VERSION,$REVISION) = pb_version_init();
37
38=pod
39
40=head1 NAME
41
42ProjectBuilder::VE, part of the project-builder.org - module dealing with Virtual Environment
43
44=head1 DESCRIPTION
45
46This modules provides functions to deal with Virtual Environements (VE), aka chroot/containers.
47
48=head1 SYNOPSIS
49
50  use ProjectBuilder::VE;
51
52  #
53  # Return information on the running distro
54  #
55  my $pbos = pb_ve_launch();
56
57=head1 USAGE
58
59=over 4
60
61=item B<pb_ve_launch>
62
63This function launches a VE, creating it if necessary using multiple external potential tools.
64
65=cut
66
67sub pb_ve_launch {
68
69my $v = shift;
70my $pbforce = shift;            # Which step are we in (0: create, 1: setup, 2: build, 3: use)
71my $locsnap = shift;
72my $vetype = shift;
73my $pbimage = shift;
74
75my $docrepo = "";           # By default no repository for docker available
76
77pb_log(2,"Entering pb_ve_launch at step $pbforce for type $vetype\n");
78# Get distro context
79my $pbos = pb_distro_get_context($v);
80
81$vetype = pb_ve_get_type($vetype);
82my ($vepath) = pb_conf_get("vepath");
83
84if ($vetype eq "docker") {
85    $docrepo = pb_ve_docker_repo();
86}
87
88# Architecture consistency
89my $arch = pb_get_arch();
90if ($arch ne $pbos->{'arch'}) {
91    die "Unable to launch a VE of architecture $pbos->{'arch'} on a $arch platform" unless (($pbos->{'arch'} =~ /i?86/o) && ($arch eq "x86_64"));
92}
93
94# If we are already root (from pbmkbm e.g.) don't use sudo, just call the command
95my $sudocmd="";
96if ($EFFECTIVE_USER_ID != 0) {
97    $sudocmd ="sudo ";
98    foreach my $proxy (qw/http_proxy https_proxy ftp_proxy/) {
99        if (defined $ENV{$proxy}) {
100            open(CMD,"sudo sh -c 'echo \$$proxy' |") or die "can't run sudo sh?: $!";
101            $_ = <CMD>;
102            chomp();
103            die "sudo not passing through env var $proxy; '$ENV{$proxy}' != '$_'\nAdd line Defaults:`whoami` env_keep += \"$proxy\" to sudoers file?" unless $_ eq $ENV{$proxy};
104            close(CMD);
105        }
106    }
107}
108
109# Handle cross arch on Intel based platforms
110$sudocmd = "setarch i386 $sudocmd" if (($pbos->{'arch'} =~ /i[3456]86/) && ($arch eq 'x86_64'));
111
112confess "No vepath value defined, please add one to your project yml file" if ((not defined $vepath) or (not defined $vepath->{$ENV{'PBPROJ'}})); 
113my $root = pb_path_expand($vepath->{$ENV{'PBPROJ'}});
114   
115if (($vetype eq "chroot") || ($vetype eq "schroot") || ($vetype eq "docker")) {
116
117    # We need to avoid umask propagation to the VE
118    umask 0022;
119
120    # We can probably only get those params now we have the distro context
121    my ($rbsb4pi,$rbspi,$vesnap,$oscodename,$osmindep,$verebuild,$rbsmirrorsrv) = pb_conf_get_if("rbsb4pi","rbspi","vesnap","oscodename","osmindep","verebuild","rbsmirrorsrv");
122
123    if (((((defined $verebuild) && (defined $verebuild->{$ENV{'PBPROJ'}}) && ($verebuild->{$ENV{'PBPROJ'}} =~ /true/i)) || ($pbforce == 0)) && ($vetype ne "docker"))
124        # For docker we may have a reference image that we'll use
125        || (($vetype eq "docker") && ($pbforce == 0) && ((not defined $pbimage) || ($pbimage eq "")))) {
126
127        my ($verpmtype,$vedebtype) = pb_conf_get("verpmtype","vedebtype");
128        my ($rbsopt1) = pb_conf_get_if("rbsopt");
129
130        # We have to rebuild the chroot
131        if ($pbos->{'type'} eq "rpm") {
132   
133            # Which tool is used
134            my $verpmstyle = $verpmtype->{$ENV{'PBPROJ'}};
135            die "No verpmtype defined for $ENV{'PBPROJ'}" unless (defined $verpmstyle);
136   
137            # Get potential rbs option
138            my $rbsopt = "";
139            if (defined $rbsopt1) {
140                if (defined $rbsopt1->{$verpmstyle}) {
141                    $rbsopt = $rbsopt1->{$verpmstyle};
142                } elsif (defined $rbsopt1->{$ENV{'PBPROJ'}}) {
143                    $rbsopt = $rbsopt1->{$ENV{'PBPROJ'}};
144                } else {
145                    $rbsopt = "";
146                }
147            }
148   
149            my $postinstall = pb_ve_get_postinstall($pbos,$rbspi,$verpmstyle);
150            if ($verpmstyle eq "rinse") {
151                # Need to reshape the mirrors generated with local before-post-install script
152                my $b4post = "--before-post-install ";
153                my $postparam = pb_distro_get_param($pbos,$rbsb4pi);
154                if ($postparam eq "") {
155                    $b4post = "";
156                } else {
157                    $b4post .= $postparam;
158                }
159   
160                # Need to reshape the package list for pb
161                my $addpkgs;
162                $postparam = "";
163                $postparam .= pb_distro_get_param($pbos,$osmindep);
164                if ($postparam eq "") {
165                    $addpkgs = "";
166                } else {
167                    my $pkgfile = "$ENV{'PBTMP'}/addpkgs.lis";
168                    open(PKG,"> $pkgfile") || die "Unable to create $pkgfile";
169                    foreach my $p (split(/,/,$postparam)) {
170                        print PKG "$p\n";
171                    }
172                    close(PKG);
173                    $addpkgs = "--add-pkg-list $pkgfile";
174                }
175   
176                my $rinseverb = "";
177                $rinseverb = "--verbose" if ($pbdebug gt 0);
178                my ($rbsconf) = pb_conf_get("rbsconf");
179   
180                my $command = pb_check_req("rinse",0);
181                pb_system("$sudocmd $command --directory \"$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}\" --arch \"$pbos->{'arch'}\" --distribution \"$pbos->{'name'}-$pbos->{'version'}\" --config \"$rbsconf->{$ENV{'PBPROJ'}}\" $b4post $postinstall $rbsopt $addpkgs $rinseverb","Creating the rinse VE for $pbos->{'name'}-$pbos->{'version'} ($pbos->{'arch'})", "verbose");
182            } elsif ($verpmstyle eq "rpmbootstrap") {
183                my $rbsverb = "";
184                foreach my $i (1..$pbdebug) {
185                    $rbsverb .= " -v";
186                }
187                my $addpkgs = "";
188                my $postparam = "";
189                $postparam .= pb_distro_get_param($pbos,$osmindep);
190                if ($postparam eq "") {
191                    $addpkgs = "";
192                } else {
193                    $addpkgs = "-a $postparam";
194                }
195                my $command = pb_check_req("rpmbootstrap",0);
196                pb_system("$sudocmd $command $rbsopt $postinstall $addpkgs $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} $rbsverb","Creating the rpmbootstrap VE for $pbos->{'name'}-$pbos->{'version'} ($pbos->{'arch'})", "verbose");
197                pb_system("$sudocmd /bin/umount $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/proc","Umounting stale /proc","mayfail") if (-f "$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/proc/cpuinfo");
198            } elsif ($verpmstyle eq "mock") {
199                my ($rbsconf) = pb_conf_get("rbsconf");
200                my $command = pb_check_req("mock",0);
201                pb_system("$sudocmd $command --init --resultdir=\"/tmp\" --configdir=\"$rbsconf->{$ENV{'PBPROJ'}}\" -r $v $rbsopt","Creating the mock VE for $pbos->{'name'}-$pbos->{'version'} ($pbos->{'arch'})");
202                # Once setup we need to install some packages, the pb account, ...
203                pb_system("$sudocmd $command --install --configdir=\"$rbsconf->{$ENV{'PBPROJ'}}\" -r $v su","Configuring the mock VE");
204            } else {
205                die "Unknown verpmtype type $verpmstyle. Report to dev team";
206            }
207        } elsif ($pbos->{'type'} eq "deb") {
208            my $vedebstyle = $vedebtype->{$ENV{'PBPROJ'}};
209       
210            my $codename = pb_distro_get_param($pbos,$oscodename);
211            my $postparam = "";
212            my $addpkgs;
213            $postparam .= pb_distro_get_param($pbos,$osmindep);
214            if ($postparam eq "") {
215                $addpkgs = "";
216            } else {
217                $addpkgs = "--include $postparam";
218            }
219            my $debmir = "";
220            $debmir .= pb_distro_get_param($pbos,$rbsmirrorsrv);
221   
222            # Get potential rbs option
223            my $rbsopt = "";
224            if (defined $rbsopt1) {
225                if (defined $rbsopt1->{$vedebstyle}) {
226                    $rbsopt = $rbsopt1->{$vedebstyle};
227                } elsif (defined $rbsopt1->{$ENV{'PBPROJ'}}) {
228                    $rbsopt = $rbsopt1->{$ENV{'PBPROJ'}};
229                } else {
230                    $rbsopt = "";
231                }
232            }
233   
234            # debootstrap works with amd64 not x86_64
235            my $debarch = $pbos->{'arch'};
236            $debarch = "amd64" if ($pbos->{'arch'} eq "x86_64");
237            if ($vedebstyle eq "debootstrap") {
238                my $dbsverb = "";
239                $dbsverb = "--verbose" if ($pbdebug gt 0);
240       
241                # Some perl modules are in Universe on Ubuntu
242                $rbsopt .= " --components=main,universe" if ($pbos->{'name'} eq "ubuntu");
243       
244                my $cmd1 = pb_check_req("mkdir",0);
245                my $cmd2 = pb_check_req("debootstrap",0);
246                pb_system("$sudocmd $cmd1 -p $root/$pbos->{name}/$pbos->{version}/$pbos->{arch} ; $sudocmd $cmd2 $dbsverb $rbsopt --arch=$debarch $addpkgs $codename \"$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}\" $debmir","Creating the debootstrap VE for $pbos->{'name'}-$pbos->{'version'} ($pbos->{'arch'})", "verbose");
247                # debootstrap doesn't create an /etc/hosts file
248                if (! -f "$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/etc/hosts" ) {
249                    my $cmd = pb_check_req("cp",0);
250                    pb_system("$sudocmd $cmd /etc/hosts $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/etc/hosts");
251                }
252            } else {
253                die "Unknown vedebtype type $vedebstyle. Report to dev team";
254            }
255        } elsif ($pbos->{'type'} eq "ebuild") {
256            die "Please teach the dev team how to build gentoo chroot";
257        } elsif ($pbos->{'type'} eq "apk") {
258            die "Please teach the dev team how to build alpine chroot";
259        } else {
260            die "Unknown distribution type $pbos->{'type'}. Report to dev team";
261        }
262    }
263
264    # Test if an existing snapshot exists and use it if appropriate
265    # And also use it if no local extracted VE is present
266    if ((-f "$root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz") &&
267    (((defined $vesnap->{$v}) && ($vesnap->{$v} =~ /true/i)) ||
268        ((defined $vesnap->{$ENV{'PBPROJ'}}) && ($vesnap->{$ENV{'PBPROJ'}} =~ /true/i))) &&
269        ($locsnap eq 1) &&
270        ($vetype ne "docker") &&
271        (! -d "$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}")) {
272            my $cmd1 = pb_check_req("rm",0);
273            my $cmd2 = pb_check_req("mkdir",0);
274            my $cmd3 = pb_check_req("tar",0);
275            pb_system("$sudocmd $cmd1 -rf $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} ; $sudocmd $cmd2 -p $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} ; $sudocmd $cmd3 xz  -C $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} -f $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz","Extracting snapshot of $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz under $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}");
276    }
277
278    if ($vetype ne "docker") {
279        # Fix modes to allow access to the VE for pb user
280        my $command = pb_check_req("chmod",0);
281        pb_system("$sudocmd $command 755 $root/$pbos->{'name'} $root/$pbos->{'name'}/$pbos->{'version'} $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}","Fixing permissions");
282    }
283
284    # If docker, create the image and remove the now temp dir except if we had one already
285    if (($vetype eq "docker") && ($pbforce == 0)) {
286        my $cmd1 = pb_check_req("docker",0);
287        # step 0 : nothing at creation -> tag n-v-a (made below)
288
289        if ((not defined $pbimage) || ($pbimage eq "")) {
290            # Snaphot the VE to serve as an input for docker
291            pb_ve_snap($pbos,$root);
292            # Create the docker image from the previous bootstrap
293            # Need sudo to be able to create all files correctly
294            # TODO: check before that the image doesn't already exist in the docker registry
295           
296            my $pbimage = "$docrepo$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}";
297            pb_system("$sudocmd $cmd1 import - $pbimage < $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz");
298            pb_system("$cmd1 push $pbimage");
299        } else {
300            # If we pass a parameter to -i, this is the name of an existing upstream image for that distro-ver-arch
301            # That container should then be setup correctly which may not be the case yet
302            #
303            # We can probably only get those params now we have the distro context
304            my ($osmindep) = pb_conf_get_if("osmindep");
305            my $pkgs = pb_distro_get_param($pbos,$osmindep);
306            $pkgs =~ s/,/ /g;
307            my $tmpd = "$ENV{'PBTMP'}/Dockerfile";
308            open(DOCKER, "> $tmpd") || die "Unable to create the docker file $tmpd";
309            print DOCKER "FROM $pbimage\n";
310            print DOCKER "MAINTAINER project-builder.org aka pb\n";
311            print DOCKER "ENV ftp_proxy $ENV{'ftp_proxy'}\n" if (defined $ENV{'ftp_proxy'});
312            print DOCKER "ENV http_proxy $ENV{'http_proxy'}\n" if (defined $ENV{'http_proxy'});
313            print DOCKER "ENV https_proxy $ENV{'https_proxy'}\n" if (defined $ENV{'https_proxy'});
314            # We are root in that container so no need to sudo, which is present potentially
315            my $cmd2 = $pbos->{'install'};
316            $cmd2 =~ s/sudo //g;
317            print DOCKER "RUN $cmd2 $pkgs\n";
318            close(DOCKER);
319            pb_system("cd $ENV{'PBTMP'} ; $sudocmd $cmd1 build -t $docrepo$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} .","Installing dependencies $pkgs in Docker container $docrepo$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}");
320            unlink($tmpd);
321        }
322    }
323
324    # Nothing more to do for VE. No real launch
325} else {
326    die "VE of type $vetype not supported. Report to the dev team";
327}
328}
329
330#
331# Return the postinstall line if needed
332#
333
334sub pb_ve_get_postinstall {
335
336my $pbos = shift;
337my $rbspi = shift;
338my $vestyle = shift;
339my $post = "";
340
341# Do we have a local post-install script
342if ($vestyle eq "rinse") {
343    $post = "--post-install ";
344} elsif ($vestyle eq "rpmbootstrap") {
345    $post = "-s ";
346}
347
348my $postparam = pb_distro_get_param($pbos,$rbspi);
349if ($postparam eq "") {
350    $post = "";
351} else {
352    $post .= $postparam;
353}
354return($post);
355}
356
357# Snapshot the VE
358sub pb_ve_snap {
359
360my $pbos = shift;
361my $root = shift;
362my $tpdir = "$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}";
363pb_system("sudo tar cz -C $tpdir -f $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz .","Creating a snapshot of $tpdir");
364}
365
366# Returns the docker registry to interact with
367sub pb_ve_docker_registry {
368
369my $dockerreg = shift;
370my $wget = pb_check_req("wget",0);
371my ($scheme, $account, $host, $port, $path) = pb_get_uri($dockerreg);
372my $docreg = $scheme."://";
373$docreg .= $account."@" if ((defined $account) && ($account ne ""));
374$docreg .= $host;
375$docreg .= ":$port" if ((defined $port) && ($port ne ""));
376open(FD,"$wget $docreg -q -O -|") || die "Unable to talk to the docker registry $docreg";
377my $found = undef;
378while (<FD>) {
379    $found = 1 if (/docker-registry/);
380}
381close(FD);
382die "No correct docker-registry answering at $docreg. Please check your configuration" if (not defined $found);
383#
384return($docreg);
385}
386
387sub pb_ve_docker_get_image {
388
389my $pbimage = shift;
390my $found =undef;
391
392die "Unable to handle an undef docker image" if (not defined $pbimage);
393
394# Check that this docker image exists
395my $cmd1 = pb_check_req("docker",0);
396open(CMD, "$cmd1 images |") || die "Unable to get docker image list";
397my ($repo, $tag, $id, $dummy);
398while (<CMD>) {
399    ($repo, $tag, $id, $dummy) = split(/\s+/,$_,4);
400    $found = $id if ("$repo:$tag" eq $pbimage);
401}
402close(CMD);
403return($found);
404}
405
406sub pb_ve_get_type {
407
408my $vetype = shift;
409
410# Get VE context
411if (not defined $vetype) {
412    my ($ptr) = pb_conf_get("vetype");
413    $vetype = $ptr->{$ENV{'PBPROJ'}};
414}
415confess "No vetype defined for $ENV{'PBPROJ'}" unless (defined $vetype);
416pb_log(1, "Using vetype $vetype for $ENV{'PBPROJ'}\n");
417return($vetype);
418}
419
420# Returns the docker repository to interact with
421sub pb_ve_docker_repo {
422
423my $docrepo = "";
424# Check acces to registry
425my ($dockerregistry) = pb_conf_get_if("dockerregistry");
426if ((defined $dockerregistry) && (defined $dockerregistry->{$ENV{'PBPROJ'}})) {
427    pb_ve_docker_registry($dockerregistry->{$ENV{'PBPROJ'}});
428    my ($scheme, $account, $host, $port, $path) = pb_get_uri($dockerregistry->{$ENV{'PBPROJ'}}).":";
429    $docrepo .= $host;
430    $docrepo .= ":$port" if ((defined $port) && ($port ne ""));
431    $docrepo .= "$path";
432} else {
433    my ($dockerrepository) = pb_conf_get("dockerrepository");
434    $docrepo = $dockerrepository->{$ENV{'PBPROJ'}}.":";
435}
436pb_log(1,"Using Docker Repository $docrepo\n");
437return($docrepo);
438}
439
440
441=head1 WEB SITES
442
443The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
444
445=head1 USER MAILING LIST
446
447None exists for the moment.
448
449=head1 AUTHORS
450
451The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
452
453=head1 COPYRIGHT
454
455Project-Builder.org is distributed under the GPL v2.0 license
456described in the file C<COPYING> included with the distribution.
457
458=cut
459
460
4611;
Note: See TracBrowser for help on using the repository browser.