1####################################################################################################################################
2# ContainerTest.pm - Build containers for testing and documentation
3####################################################################################################################################
4package pgBackRestTest::Common::ContainerTest;
5
6####################################################################################################################################
7# Perl includes
8####################################################################################################################################
9use strict;
10use warnings FATAL => qw(all);
11use Carp qw(confess longmess);
12use English '-no_match_vars';
13
14use Cwd qw(abs_path);
15use Digest::SHA qw(sha1_hex);
16use Exporter qw(import);
17    our @EXPORT = qw();
18use File::Basename qw(dirname);
19use Getopt::Long qw(GetOptions);
20
21use pgBackRestDoc::Common::Log;
22use pgBackRestDoc::Common::String;
23use pgBackRestDoc::ProjectInfo;
24
25use pgBackRestTest::Common::ExecuteTest;
26use pgBackRestTest::Common::VmTest;
27
28####################################################################################################################################
29# User/group definitions
30####################################################################################################################################
31use constant TEST_USER                                              => getpwuid($UID) . '';
32    push @EXPORT, qw(TEST_USER);
33use constant TEST_USER_ID                                           => $UID;
34    push @EXPORT, qw(TEST_USER_ID);
35use constant TEST_GROUP                                             => getgrgid((getpwnam(TEST_USER))[3]) . '';
36    push @EXPORT, qw(TEST_GROUP);
37use constant TEST_GROUP_ID                                          => getgrnam(TEST_GROUP) . '';
38    push @EXPORT, qw(TEST_GROUP_ID);
39
40####################################################################################################################################
41# Cert file constants
42####################################################################################################################################
43use constant CERT_FAKE_PATH                                         => '/etc/fake-cert';
44use constant CERT_FAKE_CA                                           => CERT_FAKE_PATH . '/ca.crt';
45    push @EXPORT, qw(CERT_FAKE_CA);
46use constant CERT_FAKE_SERVER                                       => CERT_FAKE_PATH . '/server.crt';
47    push @EXPORT, qw(CERT_FAKE_SERVER);
48use constant CERT_FAKE_SERVER_KEY                                   => CERT_FAKE_PATH . '/server.key';
49    push @EXPORT, qw(CERT_FAKE_SERVER_KEY);
50
51####################################################################################################################################
52# Container Debug - speeds container debugging by splitting each section into a separate intermediate container
53####################################################################################################################################
54use constant CONTAINER_DEBUG                                        => false;
55
56####################################################################################################################################
57# Store cache container checksums
58####################################################################################################################################
59my $hContainerCache;
60
61####################################################################################################################################
62# Container repo - defines the Docker repository where the containers will be located
63####################################################################################################################################
64sub containerRepo
65{
66    return PROJECT_EXE . qw(/) . 'test';
67}
68
69push @EXPORT, qw(containerRepo);
70
71####################################################################################################################################
72# Section header
73####################################################################################################################################
74sub sectionHeader
75{
76    return (CONTAINER_DEBUG ? "\n\nRUN \\\n" : " && \\\n\n");
77}
78
79####################################################################################################################################
80# Write the Docker container
81####################################################################################################################################
82sub containerWrite
83{
84    my $oStorageDocker = shift;
85    my $strTempPath = shift;
86    my $strOS = shift;
87    my $strTitle = shift;
88    my $strImageParent = shift;
89    my $strImage = shift;
90    my $strCopy = shift;
91    my $strScript = shift;
92    my $bForce = shift;
93
94    my $strTag = containerRepo() . ":${strImage}";
95
96    $strScript =
97        "# ${strTitle} Container\n" .
98        "FROM ${strImageParent}" .
99        (defined($strCopy) ? "\n\n${strCopy}" : '') .
100        (defined($strScript) && $strScript ne ''?
101            "\n\nRUN echo '" . (CONTAINER_DEBUG ? 'DEBUG' : 'OPTIMIZED') . " BUILD'" . $strScript : '');
102
103    # Search for the image in the cache
104    my $strScriptSha1;
105    my $bCached = false;
106
107    if ($strImage =~ /\-base$/)
108    {
109        $strScriptSha1 = sha1_hex($strScript);
110
111        foreach my $strBuild (reverse(keys(%{$hContainerCache})))
112        {
113            if (defined($hContainerCache->{$strBuild}{hostArch()}{$strOS}) &&
114                $hContainerCache->{$strBuild}{hostArch()}{$strOS} eq $strScriptSha1)
115            {
116                &log(INFO, "Using cached ${strTag}-${strBuild} image (${strScriptSha1}) ...");
117
118                $strScript =
119                    "# ${strTitle} Container\n" .
120                    "FROM ${strTag}-${strBuild}";
121
122                $bCached = true;
123            }
124        }
125    }
126
127    if (!$bCached)
128    {
129        &log(INFO, "Building ${strTag} image" . (defined($strScriptSha1) ? " (${strScriptSha1})" : '') . ' ...');
130    }
131
132    # Write the image
133    $oStorageDocker->put("${strTempPath}/${strImage}", trim($strScript) . "\n");
134    executeTest('docker build' . (defined($bForce) && $bForce ? ' --no-cache' : '') .
135                " -f ${strTempPath}/${strImage} -t ${strTag} ${strTempPath}",
136                {bSuppressStdErr => true});
137}
138
139####################################################################################################################################
140# User/group creation
141####################################################################################################################################
142sub groupCreate
143{
144    my $strOS = shift;
145    my $strName = shift;
146    my $iId = shift;
147
148    return "groupadd -f -g${iId} ${strName}";
149}
150
151sub userCreate
152{
153    my $strOS = shift;
154    my $strName = shift;
155    my $iId = shift;
156    my $strGroup = shift;
157
158    my $oVm = vmGet();
159
160    if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
161    {
162        return "adduser -g${strGroup} -u${iId} -n ${strName}";
163    }
164    elsif ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_DEBIAN)
165    {
166        return "adduser --uid=${iId} --ingroup=${strGroup} --disabled-password --gecos \"\" ${strName}";
167    }
168
169    confess &log(ERROR, "unable to create user for os '${strOS}'");
170}
171
172####################################################################################################################################
173# Setup SSH
174####################################################################################################################################
175sub sshSetup
176{
177    my $strOS = shift;
178    my $strUser = shift;
179    my $strGroup = shift;
180    my $bControlMaster = shift;
181
182    my $strUserPath = $strUser eq 'root' ? "/${strUser}" : "/home/${strUser}";
183
184    my $strScript = sectionHeader() .
185        "# Setup SSH\n" .
186        "    mkdir ${strUserPath}/.ssh && \\\n" .
187        "    echo '-----BEGIN RSA PRIVATE KEY-----' > ${strUserPath}/.ssh/id_rsa && \\\n" .
188        "    echo 'MIICXwIBAAKBgQDR0yJsZW5d5LcqteiOtv8d+FFeFFHDPI0VTcTOdMn1iDiIP1ou' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
189        "    echo 'X3Q2OyNjsBaDbsRJd+sp9IRq1LKX3zsBcgGZANwm0zduuNEPEU94ajS/uRoejIqY' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
190        "    echo '/XkKOpnEF6ZbQ2S7TaE4sWeGLvba7kUFs0QTOO+N+nV2dMbdqZf6C8lazwIDAQAB' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
191        "    echo 'AoGBAJXa6xzrnFVmwgK5BKzYuX/YF5TPgk2j80ch0ct50buQXH/Cb0/rUH5i4jWS' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
192        "    echo 'T6Hy/DFUehnuzpvV6O9auTOhDs3BhEKFRuRLn1nBwTtZny5Hh+cw7azUCEHFCJlz' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
193        "    echo 'makCrVbgawtno6oU/pFgQm1FcxD0f+Me5ruNcLHqUZsPQwkRAkEA+8pG+ckOlz6R' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
194        "    echo 'AJLIHedmfcrEY9T7sfdo83bzMOz8H5soUUP4aOTLJYCla1LO7JdDnXMGo0KxaHBP' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
195        "    echo 'l8j5zDmVewJBANVVPDJr1w37m0FBi37QgUOAijVfLXgyPMxYp2uc9ddjncif0063' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
196        "    echo '0Wc0FQefoPszf3CDrHv/RHvhHq97jXDwTb0CQQDgH83NygoS1r57pCw9chzpG/R0' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
197        "    echo 'aMEiSPhCvz757fj+qT3aGIal2AJ7/2c/gRZvwrWNETZ3XIZOUKqIkXzJLPjBAkEA' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
198        "    echo 'wnP799W2Y8d4/+VX2pMBkF7lG7sSviHEq1sP2BZtPBRQKSQNvw3scM7XcGh/mxmY' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
199        "    echo 'yx0qpqfKa8SKbNgI1+4iXQJBAOlg8MJLwkUtrG+p8wf69oCuZsnyv0K6UMDxm6/8' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
200        "    echo 'cbvfmvODulYFaIahaqHWEZoRo5CLYZ7gN43WHPOrKxdDL78=' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
201        "    echo '-----END RSA PRIVATE KEY-----' >> ${strUserPath}/.ssh/id_rsa && \\\n" .
202        "    echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDR0yJsZW5d5LcqteiOtv8d+FFeFFHDPI0VTcTOdMn1iDiIP1ouX3Q2OyNjsBaDbsRJd+sp9I" .
203             "Rq1LKX3zsBcgGZANwm0zduuNEPEU94ajS/uRoejIqY/XkKOpnEF6ZbQ2S7TaE4sWeGLvba7kUFs0QTOO+N+nV2dMbdqZf6C8lazw== " .
204             "user\@pgbackrest-test' > ${strUserPath}/.ssh/authorized_keys && \\\n" .
205        "    echo 'Host *' > ${strUserPath}/.ssh/config && \\\n" .
206        "    echo '    StrictHostKeyChecking no' >> ${strUserPath}/.ssh/config && \\\n";
207
208    if ($bControlMaster)
209    {
210        $strScript .=
211            "    echo '    ControlMaster auto' >> ${strUserPath}/.ssh/config && \\\n" .
212            "    echo '    ControlPath /tmp/\%r\@\%h:\%p' >> ${strUserPath}/.ssh/config && \\\n" .
213            "    echo '    ControlPersist 30' >> ${strUserPath}/.ssh/config && \\\n";
214    }
215
216    $strScript .=
217        "    chown -R ${strUser}:${strGroup} ${strUserPath}/.ssh && \\\n" .
218        "    chmod 700 ${strUserPath}/.ssh && \\\n" .
219        "    chmod 600 ${strUserPath}/.ssh/*";
220
221    return $strScript;
222}
223
224####################################################################################################################################
225# Cert Setup
226####################################################################################################################################
227sub certSetup
228{
229    my $strOS = shift;
230
231    my $strScript =
232        sectionHeader() .
233        "# Generate fake certs\n" .
234        "    mkdir -p -m 755 /etc/fake-cert && \\\n" .
235        "    cd /etc/fake-cert && \\\n" .
236        "    openssl genrsa -out ca.key 2048 && \\\n" .
237        "    openssl req -new -x509 -extensions v3_ca -key ca.key -out ca.crt -days 99999 \\\n" .
238        "        -subj \"/C=US/ST=Country/L=City/O=Organization/CN=pgbackrest.org\" && \\\n" .
239        "    openssl genrsa -out server.key 2048 && \\\n" .
240        "    openssl req -new -key server.key -out server.csr \\\n" .
241        "        -subj \"/C=US/ST=Country/L=City/O=Organization/CN=*.pgbackrest.org\" && \\\n" .
242        "    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 99999 \\\n" .
243        "        -sha256 && \\\n" .
244        "    chmod 644 /etc/fake-cert/* && \\\n";
245
246    my $rhVm = vmGet();
247
248    if ($rhVm->{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
249    {
250        $strScript .=
251            "    cp /etc/fake-cert/pgbackrest-test-ca.crt /etc/pki/ca-trust/source/anchors && \\\n" .
252            "    update-ca-trust extract";
253    }
254    elsif ($rhVm->{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_DEBIAN)
255    {
256        $strScript .=
257            "    cp /etc/fake-cert/pgbackrest-test-ca.crt /usr/local/share/ca-certificates && \\\n" .
258            "    update-ca-certificates";
259    }
260    else
261    {
262        confess &log(ERROR, "unable to install certificate for $rhVm->{$strOS}{&VM_OS_BASE}");
263    }
264
265    return $strScript;
266}
267
268####################################################################################################################################
269# Entry point setup
270####################################################################################################################################
271sub entryPointSetup
272{
273    my $strOS = shift;
274
275    my $strScript =
276        "\n\n# Start SSH when container starts\n" .
277        'ENTRYPOINT ';
278
279    my $oVm = vmGet();
280
281    if ($oVm->{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
282    {
283        $strScript .= '/usr/sbin/sshd -D';
284    }
285    else
286    {
287        $strScript .= 'service ssh restart && bash';
288    }
289
290    return $strScript;
291}
292
293####################################################################################################################################
294# Build containers
295####################################################################################################################################
296sub containerBuild
297{
298    my $oStorageDocker = shift;
299    my $strVm = shift;
300    my $bVmForce = shift;
301
302    # Create temp path
303    my $strTempPath = $oStorageDocker->pathGet('test/result/docker');
304    $oStorageDocker->pathCreate($strTempPath, {strMode => '0770', bIgnoreExists => true, bCreateParent => true});
305
306    # Load container definitions from yaml
307    require YAML::XS;
308    YAML::XS->import(qw(Load));
309
310    $hContainerCache = Load(${$oStorageDocker->get($oStorageDocker->pathGet('test/container.yaml'))});
311
312    # Remove old images on force
313    if ($bVmForce)
314    {
315        my $strRegExp = '^' . containerRepo();
316
317        if ($strVm ne 'all')
318        {
319            $strRegExp .= "\:${strVm}-";
320        }
321
322        executeTest("rm -f ${strTempPath}/" . ($strVm eq 'all' ? '*' : "${strVm}-*"));
323        imageRemove("${strRegExp}.*");
324    }
325
326    # VM Images
327    ################################################################################################################################
328    my $oVm = vmGet();
329
330    foreach my $strOS ($strVm eq 'all' ? VM_LIST : ($strVm))
331    {
332        my $oOS = $oVm->{$strOS};
333        my $bDocBuild = false;
334
335        # Deprecated OSs can only be used to build packages
336        my $bDeprecated = $oVm->{$strOS}{&VM_DEPRECATED} ? true : false;
337
338        # Base image
339        ###########################################################################################################################
340        my $strImageParent = "$$oVm{$strOS}{&VM_IMAGE}";
341        my $strImage = "${strOS}-base";
342        my $strCopy = undef;
343
344        #---------------------------------------------------------------------------------------------------------------------------
345        my $strScript = sectionHeader() .
346            "# Install packages\n";
347
348        if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
349        {
350            if ($strOS eq VM_CO7)
351            {
352                $strScript .=
353                    "    yum -y install centos-release-scl-rh epel-release && \\\n";
354            }
355
356            $strScript .=
357                "    yum -y update && \\\n" .
358                "    yum -y install openssh-server openssh-clients wget sudo valgrind git \\\n" .
359                "        perl perl-Digest-SHA perl-DBD-Pg perl-YAML-LibYAML openssl \\\n" .
360                "        gcc make perl-ExtUtils-MakeMaker perl-Test-Simple openssl-devel perl-ExtUtils-Embed rpm-build \\\n" .
361                "        libyaml-devel zlib-devel libxml2-devel lz4-devel lz4 bzip2-devel bzip2 perl-JSON-PP";
362        }
363        else
364        {
365            $strScript .=
366                "    export DEBCONF_NONINTERACTIVE_SEEN=true DEBIAN_FRONTEND=noninteractive && \\\n" .
367                "    apt-get update && \\\n" .
368                "    apt-get install -y --no-install-recommends openssh-server wget sudo gcc make valgrind git \\\n" .
369                "        libdbd-pg-perl libhtml-parser-perl libssl-dev libperl-dev \\\n" .
370                "        libyaml-libyaml-perl tzdata devscripts lintian libxml-checker-perl txt2man debhelper \\\n" .
371                "        libppi-html-perl libtemplate-perl libtest-differences-perl zlib1g-dev libxml2-dev pkg-config \\\n" .
372                "        libbz2-dev bzip2 libyaml-dev libjson-pp-perl liblz4-dev liblz4-tool gnupg";
373        }
374
375        # Add zst command-line tool and development libs when available
376        if (vmWithZst($strOS))
377        {
378            if ($oVm->{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
379            {
380                $strScript .= ' zstd libzstd-devel';
381            }
382            else
383            {
384                $strScript .= ' zstd libzstd-dev';
385            }
386        }
387
388        # If no specific version of lcov is requested then install the default package
389        if (!defined($oVm->{$strOS}{&VMDEF_LCOV_VERSION}))
390        {
391            $strScript .= ' lcov';
392        }
393
394        #---------------------------------------------------------------------------------------------------------------------------
395        $strScript .= sectionHeader() .
396            "# Regenerate SSH keys\n" .
397            "    rm -f /etc/ssh/ssh_host_rsa_key* && \\\n" .
398            "    ssh-keygen -t rsa -b 1024 -f /etc/ssh/ssh_host_rsa_key";
399
400        if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_DEBIAN)
401        {
402            $strScript .= sectionHeader() .
403                "# Fix root tty\n" .
404                "    sed -i 's/^mesg n/tty -s \\&\\& mesg n/g' /root/.profile";
405
406            $strScript .= sectionHeader() .
407                "# Suppress dpkg interactive output\n" .
408                "    rm /etc/apt/apt.conf.d/70debconf";
409        }
410
411        #---------------------------------------------------------------------------------------------------------------------------
412        my $strCertPath = 'test/certificate';
413        my $strCertName = 'pgbackrest-test';
414
415        $strCopy = '# Copy Test Certificates';
416
417        foreach my $strFile ('-ca.crt', '.crt', '.key')
418        {
419            $oStorageDocker->copy("${strCertPath}/${strCertName}${strFile}", "${strTempPath}/${strCertName}${strFile}");
420            $strCopy .= "\nCOPY ${strCertName}${strFile} " . CERT_FAKE_PATH . "/${strCertName}${strFile}";
421        }
422
423        $strScript .= certSetup($strOS);
424
425        #---------------------------------------------------------------------------------------------------------------------------
426        if (defined($oVm->{$strOS}{&VMDEF_LCOV_VERSION}))
427        {
428            my $strLCovVersion = $oVm->{$strOS}{&VMDEF_LCOV_VERSION};
429            my $strLCovPath = "/root/lcov-${strLCovVersion}";
430
431            $strScript .= sectionHeader() .
432                "# Build lcov ${strLCovVersion}\n" .
433                "    wget -q -O - https://github.com/linux-test-project/lcov/releases/download/v${strLCovVersion}/" .
434                    "lcov-${strLCovVersion}.tar.gz | tar zx -C /root && \\\n" .
435                "    make -C ${strLCovPath} install && \\\n" .
436                "    rm -rf ${strLCovPath}";
437        }
438
439        #---------------------------------------------------------------------------------------------------------------------------
440        if (!$bDeprecated)
441        {
442            $strScript .=  sectionHeader() .
443                "# Install PostgreSQL packages\n";
444
445            if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
446            {
447                $strScript .=
448                    "    rpm --import http://yum.postgresql.org/RPM-GPG-KEY-PGDG && \\\n";
449
450                if ($strOS eq VM_CO7)
451                {
452                    $strScript .=
453                        "    rpm -ivh \\\n" .
454                        "        https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-" . hostArch() . "/" .
455                            "pgdg-redhat-repo-latest.noarch.rpm && \\\n";
456                }
457                elsif ($strOS eq VM_F33)
458                {
459                    $strScript .=
460                        "    rpm -ivh \\\n" .
461                        "        https://download.postgresql.org/pub/repos/yum/reporpms/F-33-" . hostArch() . "/" .
462                            "pgdg-fedora-repo-latest.noarch.rpm && \\\n";
463                }
464
465                $strScript .= "    yum -y install postgresql-devel";
466            }
467            else
468            {
469                $strScript .=
470                    "    echo 'deb http://apt.postgresql.org/pub/repos/apt/ " .
471                    $$oVm{$strOS}{&VM_OS_REPO} . '-pgdg main' . ($strOS eq VM_U20 ? ' 14' : '') .
472                        "' >> /etc/apt/sources.list.d/pgdg.list && \\\n" .
473                    "    wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \\\n" .
474                    "    apt-get update && \\\n" .
475                    "    apt-get install -y --no-install-recommends postgresql-common libpq-dev && \\\n" .
476                    "    sed -i 's/^\\#create\\_main\\_cluster.*\$/create\\_main\\_cluster \\= false/' " .
477                        "/etc/postgresql-common/createcluster.conf";
478            }
479
480            if (defined($oOS->{&VM_DB}) && @{$oOS->{&VM_DB}} > 0)
481            {
482                $strScript .= sectionHeader() .
483                    "# Install PostgreSQL\n";
484
485                if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
486                {
487                    $strScript .= "    yum -y install";
488                }
489                else
490                {
491                    $strScript .= "    apt-get install -y --no-install-recommends";
492                }
493
494                # Construct list of databases to install
495                foreach my $strDbVersion (@{$oOS->{&VM_DB}})
496                {
497                    if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
498                    {
499                        my $strDbVersionNoDot = $strDbVersion;
500                        $strDbVersionNoDot =~ s/\.//;
501
502                        $strScript .=  " postgresql${strDbVersionNoDot}-server";
503
504                        # Add development package for the latest version of postgres
505                        if ($strDbVersion eq @{$oOS->{&VM_DB}}[-1])
506                        {
507                            $strScript .=  " postgresql${strDbVersionNoDot}-devel";
508                        }
509                    }
510                    else
511                    {
512                        $strScript .= " postgresql-${strDbVersion}";
513                    }
514                }
515            }
516        }
517
518        # Add path to lastest version of postgres
519        if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
520        {
521            $strScript .=
522                "\n\nENV PATH=/usr/pgsql-" . @{$oOS->{&VM_DB}}[-1] . "/bin:\$PATH\n";
523        }
524
525        #---------------------------------------------------------------------------------------------------------------------------
526        if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_DEBIAN)
527        {
528        $strScript .=  sectionHeader() .
529            "# Cleanup\n";
530
531            $strScript .=
532                "    apt-get autoremove -y && \\\n" .
533                "    apt-get clean && \\\n" .
534                "    rm -rf /var/lib/apt/lists/*";
535        }
536
537        containerWrite(
538            $oStorageDocker, $strTempPath, $strOS, 'Base', $strImageParent, $strImage, $strCopy, $strScript, $bVmForce);
539
540        # Test image
541        ########################################################################################################################
542        if (!$bDeprecated)
543        {
544            $strImageParent = containerRepo() . ":${strOS}-base";
545            $strImage = "${strOS}-test";
546
547            $strCopy = undef;
548            $strScript = '';
549
550            #---------------------------------------------------------------------------------------------------------------------------
551            $strScript .= sectionHeader() .
552                "# Create banner to make sure pgBackRest ignores it\n" .
553                "    echo '***********************************************' >  /etc/issue.net && \\\n" .
554                "    echo 'Sample banner to make sure banners are skipped.' >> /etc/issue.net && \\\n" .
555                "    echo ''                                                >> /etc/issue.net && \\\n" .
556                "    echo 'More banner after a blank line.'                 >> /etc/issue.net && \\\n" .
557                "    echo '***********************************************' >> /etc/issue.net && \\\n" .
558                "    echo 'Banner /etc/issue.net'                           >> /etc/ssh/sshd_config";
559
560            $strScript .= sectionHeader() .
561                "# Create test user\n" .
562                '    ' . groupCreate($strOS, TEST_GROUP, TEST_GROUP_ID) . " && \\\n" .
563                '    ' . userCreate($strOS, TEST_USER, TEST_USER_ID, TEST_GROUP) . " && \\\n" .
564                '    mkdir -m 750 /home/' . TEST_USER . "/test && \\\n" .
565                '    chown ' . TEST_USER . ':' . TEST_GROUP . ' /home/' . TEST_USER . "/test";
566
567            $strScript .= sectionHeader() .
568                "# Configure sudo\n";
569
570            if ($$oVm{$strOS}{&VM_OS_BASE} eq VM_OS_BASE_RHEL)
571            {
572                $strScript .=
573                    # Don't allow sudo to disable core dump (suppresses errors, see https://github.com/sudo-project/sudo/issues/42)
574                    "    echo \"Set disable_coredump false\" >> /etc/sudo.conf && \\\n" .
575                    "    echo '%" . TEST_GROUP . "        ALL=(ALL)       NOPASSWD: ALL' > /etc/sudoers.d/" . TEST_GROUP .
576                        " && \\\n" .
577                    "    sed -i 's/^Defaults    requiretty\$/\\# Defaults    requiretty/' /etc/sudoers";
578            }
579            else
580            {
581                $strScript .=
582                    "    echo '%" . TEST_GROUP . " ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers";
583            }
584
585            $strScript .=
586                sshSetup($strOS, TEST_USER, TEST_GROUP, $$oVm{$strOS}{&VM_CONTROL_MASTER});
587
588            $strScript .=  sectionHeader() .
589                "# Make " . TEST_USER . " home dir readable\n" .
590                '    chmod g+r,g+x /home/' . TEST_USER;
591
592            $strScript .= entryPointSetup($strOS);
593
594            containerWrite(
595                $oStorageDocker, $strTempPath, $strOS, 'Test', $strImageParent, $strImage, $strCopy, $strScript, $bVmForce);
596        }
597    }
598
599    &log(INFO, "Build Complete");
600}
601
602push @EXPORT, qw(containerBuild);
603
604####################################################################################################################################
605# containerRemove
606#
607# Remove containers that match a regexp.
608####################################################################################################################################
609sub containerRemove
610{
611    my $strRegExp = shift;
612
613    foreach my $strContainer (sort(split("\n", trim(executeTest('docker ps -a --format "{{.Names}}"')))))
614    {
615        if ($strContainer =~ $strRegExp)
616        {
617            executeTest("docker rm -f ${strContainer}", {bSuppressError => true});
618        }
619    }
620}
621
622push @EXPORT, qw(containerRemove);
623
624####################################################################################################################################
625# imageRemove
626#
627# Remove images that match a regexp.
628####################################################################################################################################
629sub imageRemove
630{
631    my $strRegExp = shift;
632
633    foreach my $strImage (sort(split("\n", trim(executeTest('docker images --format "{{.Repository}}:{{.Tag}}"')))))
634    {
635        if ($strImage =~ $strRegExp)
636        {
637            &log(INFO, "Removing ${strImage} image...");
638            executeTest("docker rmi -f ${strImage}");
639        }
640    }
641}
642
6431;
644