Merge ~jslarraz/review-tools:rock-container into review-tools:master

Proposed by Jorge Sancho Larraz
Status: Merged
Merged at revision: 10f1ead2610427bf86c358437e6ee0ede4ca6b43
Proposed branch: ~jslarraz/review-tools:rock-container
Merge into: review-tools:master
Diff against target: 771 lines (+339/-333)
7 files modified
reviewtools/available.py (+3/-2)
reviewtools/common.py (+0/-139)
reviewtools/containers/rock_container.py (+26/-0)
reviewtools/containers/tar_container.py (+96/-0)
reviewtools/tests/containers/test_rock_container.py (+144/-0)
reviewtools/tests/containers/test_tar_container.py (+70/-0)
reviewtools/tests/test_common.py (+0/-192)
Reviewer Review Type Date Requested Status
Alex Murray Approve
Evan Caville Approve
Review via email: mp+467342@code.launchpad.net

Commit message

rock function to container classes

To post a comment you must log in.
Revision history for this message
Evan Caville (evancaville) wrote :

refactor looks good to me! Use of inheritance looks clean and follows similar pattern for both snaps and rocks. Thanks!

review: Approve
Revision history for this message
Alex Murray (alexmurray) wrote :

LGTM - thanks for the additional TarContainer tests as well!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/reviewtools/available.py b/reviewtools/available.py
index 94bfbe1..90f6917 100755
--- a/reviewtools/available.py
+++ b/reviewtools/available.py
@@ -24,7 +24,6 @@ from reviewtools.common import (
24 read_file_as_json_dict,24 read_file_as_json_dict,
25 MKDTEMP_PREFIX,25 MKDTEMP_PREFIX,
26 _add_error, # make a class26 _add_error, # make a class
27 get_rock_manifest,
28)27)
29import reviewtools.email as email28import reviewtools.email as email
3029
@@ -35,6 +34,7 @@ from reviewtools.store import (
35 get_faked_build_and_stage_packages,34 get_faked_build_and_stage_packages,
36)35)
37from reviewtools.usn import read_usn_db36from reviewtools.usn import read_usn_db
37from reviewtools.containers.rock_container import RockContainer
38from reviewtools.containers.snap_container import SnapContainer, ContainerException38from reviewtools.containers.snap_container import SnapContainer, ContainerException
3939
40email_update_required_text = """A scan of this %s shows that it was built with packages from the Ubuntu40email_update_required_text = """A scan of this %s shows that it was built with packages from the Ubuntu
@@ -536,7 +536,8 @@ def scan_snap(secnot_db_fn, snap_fn, with_cves=False, ignore_pockets=list()):
536def scan_rock(secnot_db_fn, rock_fn, with_cves=False, ignore_pockets=list()):536def scan_rock(secnot_db_fn, rock_fn, with_cves=False, ignore_pockets=list()):
537 """Scan rock for packages with security notices"""537 """Scan rock for packages with security notices"""
538 out = ""538 out = ""
539 man = get_rock_manifest(rock_fn)539 rock = RockContainer(rock_fn)
540 man = rock.get_rock_manifest()
540 # Since ROCKs can be based on non LTS Ubuntu releases, secnot_db should541 # Since ROCKs can be based on non LTS Ubuntu releases, secnot_db should
541 # include them (otherwise the USN db is filtered by tracked LTS releases only542 # include them (otherwise the USN db is filtered by tracked LTS releases only
542 # since that is the requirement for snaps)543 # since that is the requirement for snaps)
diff --git a/reviewtools/common.py b/reviewtools/common.py
index 5de33ee..88c6e58 100644
--- a/reviewtools/common.py
+++ b/reviewtools/common.py
@@ -25,11 +25,9 @@ import os
25from pkg_resources import resource_filename25from pkg_resources import resource_filename
26import re26import re
27import shutil27import shutil
28import stat
29import subprocess28import subprocess
30import sys29import sys
31import tarfile30import tarfile
32import tempfile
33import time31import time
34import types32import types
35import yaml33import yaml
@@ -486,16 +484,6 @@ def unsquashfs_lln_parse(lln_out):
486 return hdr, entries484 return hdr, entries
487485
488486
489def _calculate_rock_untar_uncompressed_size(rock_pkg):
490 """Calculate size of the uncompressed tar"""
491 size = 0
492 with tarfile.open(rock_pkg) as tar:
493 for tarinfo in tar:
494 size += tarinfo.size
495
496 return size
497
498
499def unsquashfs_supports_ignore_errors():487def unsquashfs_supports_ignore_errors():
500 """Detect if unsquashfs supports the -ignore-errors option"""488 """Detect if unsquashfs supports the -ignore-errors option"""
501 (rc, out) = cmd(["unsquashfs", "-help"])489 (rc, out) = cmd(["unsquashfs", "-help"])
@@ -503,112 +491,6 @@ def unsquashfs_supports_ignore_errors():
503 return "-ig[nore-errors]" in out491 return "-ig[nore-errors]" in out
504492
505493
506def is_pkg_uncompressed_size_valid(pkg_max_size, size, pkg):
507 st = os.statvfs(pkg)
508 avail = st.f_bsize * st.f_bavail * 0.9 # 90% of available space
509 if size > pkg_max_size:
510 return (
511 False,
512 "uncompressed %s is too large (%dM > %dM)"
513 % (pkg, size / 1024 / 1024, pkg_max_size / 1024 / 1024),
514 )
515 elif size > avail * 0.9:
516 return (
517 False,
518 "uncompressed %s is too large for available space (%dM > %dM)"
519 % (pkg, size / 1024 / 1024, avail / 1024 / 1024),
520 )
521 else:
522 return True, ""
523
524
525def _unpack_rock_tar(rock_pkg, dest):
526 """Unpack a tar based rock package to dest"""
527 size = _calculate_rock_untar_uncompressed_size(rock_pkg)
528
529 # Assuming MAX_UNCOMPRESSED_SIZE for rocks is same as snaps
530 # MAX_UNCOMPRESSED_SIZE
531 rock_max_size = MAX_UNCOMPRESSED_SIZE * 1024 * 1024 * 1024
532
533 valid_size, error_msg = is_pkg_uncompressed_size_valid(
534 rock_max_size, size, rock_pkg
535 )
536 if valid_size:
537 global MKDTEMP_PREFIX
538 global MKDTEMP_DIR
539 d = tempfile.mkdtemp(prefix=MKDTEMP_PREFIX, dir=MKDTEMP_DIR)
540
541 try:
542 with tarfile.open(rock_pkg) as tar:
543 # Inspecting members before extracting, since it is possible that
544 # files are created outside of path, e.g. members that have
545 # absolute filenames starting with "/" or filenames with two dots
546 # ".."
547 # https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
548 for name in tar.getnames():
549 if name.startswith("..") or name.startswith("/"):
550 logger.error(
551 "Bad path %s while extracting archive at %s"
552 % (name, rock_pkg)
553 )
554
555 tar.extractall(path=d)
556 except Exception as e:
557 logger.error("Unexpected exception while unpacking rock %s" % e)
558 if os.path.isdir(d):
559 recursive_rm(d)
560
561 if dest is None:
562 dest = d
563 else:
564 # Recursively move extracted content to original dir,
565 # keeping permissions
566 move_dir_content(d, dest)
567
568 return dest
569 else:
570 logger.error(error_msg)
571
572
573def check_dir(dest):
574 if dest is not None and os.path.exists(dest):
575 logger.error("'%s' exists. Aborting." % dest)
576
577
578def check_fn(fn):
579 if not os.path.isfile(fn):
580 logger.error("Could not find '%s'" % fn)
581 pkg = fn
582 if not pkg.startswith("/"):
583 pkg = os.path.abspath(pkg)
584 return pkg
585
586
587def check_max_pkg_size(pkg):
588 size = os.stat(pkg)[stat.ST_SIZE]
589 max = MAX_COMPRESSED_SIZE * 1024 * 1024 * 1024
590 if size > max:
591 logger.error(
592 "compressed file is too large (%dM > %dM)"
593 % (size / 1024 / 1024, max / 1024 / 1024)
594 )
595
596
597def unpack_rock(fn, dest=None):
598 """Unpack rock"""
599 pkg = check_fn(fn)
600 check_dir(dest)
601
602 # Limit the maximimum size of the package
603 check_max_pkg_size(pkg)
604
605 # check if its a tar based rock
606 if tarfile.is_tarfile(pkg):
607 return _unpack_rock_tar(fn, dest)
608
609 logger.error("Unsupported package format (not tar)")
610
611
612def open_file_read(path):494def open_file_read(path):
613 """Open specified file read-only"""495 """Open specified file read-only"""
614 try:496 try:
@@ -858,27 +740,6 @@ def read_file_as_json_dict(fn):
858 return raw740 return raw
859741
860742
861def get_rock_manifest(fn):
862 if "SNAP_USER_COMMON" in os.environ and os.path.exists(
863 os.environ["SNAP_USER_COMMON"]
864 ):
865 MKDTEMP_DIR = os.environ["SNAP_USER_COMMON"]
866 else:
867 MKDTEMP_DIR = tempfile.gettempdir()
868 unpack_tmp_dir = tempfile.mktemp(prefix=MKDTEMP_PREFIX, dir=MKDTEMP_DIR)
869
870 man_fn = "/usr/share/rocks/dpkg.query"
871 unpack_rock(fn, unpack_tmp_dir)
872
873 # since ROCK manifest is not available yet, we build a manifest using the
874 # information available in /usr/share/rocks/dpkg.query present inside the
875 # rock
876 man_yaml = build_manifest_from_rock_tar(man_fn, unpack_tmp_dir)
877 recursive_rm(unpack_tmp_dir)
878
879 return man_yaml
880
881
882def list_dir(path, dirs_only=False):743def list_dir(path, dirs_only=False):
883 """List directory items"""744 """List directory items"""
884 results = list(map(lambda x: os.path.join(path, x), os.listdir(path)))745 results = list(map(lambda x: os.path.join(path, x), os.listdir(path)))
diff --git a/reviewtools/containers/rock_container.py b/reviewtools/containers/rock_container.py
885new file mode 100644746new file mode 100644
index 0000000..6a6e799
--- /dev/null
+++ b/reviewtools/containers/rock_container.py
@@ -0,0 +1,26 @@
1import yaml
2from reviewtools.containers.tar_container import (
3 TarContainer,
4 ContainerException,
5)
6from reviewtools.common import build_manifest_from_rock_tar
7
8
9class RockContainer(TarContainer):
10 # TODO: review-tools was not checking for it. We still have some snaps in
11 # "tests/" that doesn't fit this limit and will need to be regenerated before
12 # this check can be enforced.
13 # _min_packed_size = 16 * 1024
14 _max_packed_size = 5 * 1024 * 1024 * 1024
15 _max_unpacked_size = 25 * 1024 * 1024 * 1024
16
17
18 def get_rock_manifest(self):
19
20 man_fn = "/usr/share/rocks/dpkg.query"
21 # since ROCK manifest is not available yet, we build a manifest using the
22 # information available in /usr/share/rocks/dpkg.query present inside the
23 # rock
24 man_yaml = build_manifest_from_rock_tar(man_fn, self.unpack_dir)
25
26 return man_yaml
diff --git a/reviewtools/containers/tar_container.py b/reviewtools/containers/tar_container.py
0new file mode 10064427new file mode 100644
index 0000000..674dd1b
--- /dev/null
+++ b/reviewtools/containers/tar_container.py
@@ -0,0 +1,96 @@
1import os
2import shutil
3import tarfile
4from reviewtools.common import (
5 recursive_rm,
6)
7from reviewtools.containers.base_container import BaseContainer, ContainerException
8
9
10class TarContainer(BaseContainer):
11 """
12 A container is a file that encapsulates a file system. This class extends
13 BaseContainer to handle functionality that is specific to Tar containers.
14 """
15
16 def check_container_format(self):
17 """
18 Checks that the container file has the expected format.
19
20 Raises:
21 ContainerException: This method will raise an exception if the
22 container file is not a tar container.
23 """
24 if not tarfile.is_tarfile(self.filename):
25 raise ContainerException("Unsupported package format (not tar)")
26
27
28 def calculate_unpacked_size(self) -> int:
29 """
30 Estimates the space that the unpacked directory will use by adding the
31 individual files size without extracting them.
32
33 Returns:
34 int: Estimated size in bytes of the unpacked directory
35 """
36 size = 0
37 with tarfile.open(self.filename) as tar:
38 for tarinfo in tar:
39 size += tarinfo.size
40 return size
41
42 def unpack(self, items: list = [], force: bool = False) -> str:
43 """
44 Extracts the content of the container file and returns the absolute path to
45 the newly unpacked directory.
46
47 Args:
48 force (bool, optional): If ``True``, the unpack_dir will be overwritten
49 if it already exists. Defaults to ``False``
50
51 Returns:
52 str: absolute path to the newly created unpack dir
53
54 Raises:
55 ContainerException: This method will raise an ContainerException if the
56 unpack_dir already exists and this method was called without the
57 force argument set to True
58 RuntimeError: This method will raise a RuntimeError if the extraction
59 fails for an unknown reason.
60 """
61 if self._unpack_dir is not None and not force:
62 raise ContainerException("'%s' exists. Aborting." % self._unpack_dir)
63
64 # Check disk space and space available in disk
65 self.check_unpacked_size()
66 self.check_disk_space()
67
68 # Unpack the container
69 stage_dir = os.path.join(self.tmp_dir, "stage")
70
71 with tarfile.open(self.filename) as tar:
72 # Inspecting members before extracting, since it is possible that
73 # files are created outside of path, e.g. members that have
74 # absolute filenames starting with "/" or filenames with two dots
75 # ".."
76 # https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
77 for name in tar.getnames():
78 if name.startswith("..") or name.startswith("/"):
79 raise ContainerException(
80 "Bad path %s while extracting archive at %s"
81 % (name, self.filename)
82 )
83
84 try:
85 tar.extractall(path=stage_dir)
86 except Exception as e:
87 if os.path.isdir(stage_dir):
88 recursive_rm(stage_dir)
89 raise RuntimeError("Unexpected exception while unpacking rock %s" % e)
90
91 # Update on success
92 if self._unpack_dir is not None and os.path.exists(self._unpack_dir):
93 recursive_rm(self._unpack_dir)
94 self._unpack_dir = ".".join(self.filename.split(".")[:-1])
95 shutil.move(stage_dir, self._unpack_dir)
96 return self._unpack_dir
diff --git a/reviewtools/tests/containers/test_rock_container.py b/reviewtools/tests/containers/test_rock_container.py
0new file mode 10064497new file mode 100644
index 0000000..f59b2d9
--- /dev/null
+++ b/reviewtools/tests/containers/test_rock_container.py
@@ -0,0 +1,144 @@
1import unittest
2from tempfile import mkdtemp
3from reviewtools.common import ReviewException
4from reviewtools.containers.rock_container import (
5 RockContainer,
6 ContainerException,
7)
8
9
10class TestRockContainer(unittest.TestCase):
11 """Tests for reviewtools.containers.rock_container"""
12
13 def test_get_rock_manifest(self):
14 """Test get_rock_manifest()"""
15 valid_rock_fn = "./tests/test-rock-redis_5.0-20.04.tar"
16 expected_manifest_yaml = {
17 "manifest-version": "1",
18 "os-release-id": "ubuntu",
19 "os-release-version-id": "20.04",
20 "stage-packages": [
21 "adduser=3.118ubuntu2,adduser=3.118ubuntu2",
22 "apt=2.0.2ubuntu0.2,apt=2.0.2ubuntu0.2",
23 "base-files=11ubuntu5.2,base-files=11ubuntu5.2",
24 "base-passwd=3.5.47,base-passwd=3.5.47",
25 "bash=5.0-6ubuntu1.1,bash=5.0-6ubuntu1.1",
26 "bsdutils=1:2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
27 "bzip2=1.0.8-2,bzip2=1.0.8-2",
28 "coreutils=8.30-3ubuntu2," "coreutils=8.30-3ubuntu2",
29 "dash=0.5.10.2-6,dash=0.5.10.2-6",
30 "debconf=1.5.73,debconf=1.5.73",
31 "debianutils=4.9.1,debianutils=4.9.1",
32 "diffutils=1:3.7-3,diffutils=1:3.7-3",
33 "dpkg=1.19.7ubuntu3,dpkg=1.19.7ubuntu3",
34 "e2fsprogs=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
35 "fdisk=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
36 "findutils=4.7.0-1ubuntu1," "findutils=4.7.0-1ubuntu1",
37 "gcc-10-base:amd64=10.2.0-5ubuntu1~20.04,"
38 "gcc-10=10.2.0-5ubuntu1~20.04",
39 "gpgv=2.2.19-3ubuntu2,gnupg2=2.2.19-3ubuntu2",
40 "grep=3.4-1,grep=3.4-1",
41 "gzip=1.10-0ubuntu4,gzip=1.10-0ubuntu4",
42 "hostname=3.23,hostname=3.23",
43 "init-system-helpers=1.57," "init-system-helpers=1.57",
44 "libacl1:amd64=2.2.53-6,acl=2.2.53-6",
45 "libapt-pkg6.0:amd64=2.0.2ubuntu0.2," "apt=2.0.2ubuntu0.2",
46 "libatomic1:amd64=10.2.0-5ubuntu1~20.04,"
47 "gcc-10=10.2.0-5ubuntu1~20.04",
48 "libattr1:amd64=1:2.4.48-5,attr=1:2.4.48-5",
49 "libaudit-common=1:2.8.5-2ubuntu6," "audit=1:2.8.5-2ubuntu6",
50 "libaudit1:amd64=1:2.8.5-2ubuntu6," "audit=1:2.8.5-2ubuntu6",
51 "libblkid1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
52 "libbz2-1.0:amd64=1.0.8-2,bzip2=1.0.8-2",
53 "libc-bin=2.31-0ubuntu9.1," "glibc=2.31-0ubuntu9.1",
54 "libc6:amd64=2.31-0ubuntu9.1," "glibc=2.31-0ubuntu9.1",
55 "libcap-ng0:amd64=0.7.9-2.1build1," "libcap-ng=0.7.9-2.1build1",
56 "libcom-err2:amd64=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
57 "libcrypt1:amd64=1:4.4.10-10ubuntu4," "libxcrypt=1:4.4.10-10ubuntu4",
58 "libdb5.3:amd64=5.3.28+dfsg1-0.6ubuntu2,"
59 "db5.3=5.3.28+dfsg1-0.6ubuntu2",
60 "libdebconfclient0:amd64=0.251ubuntu1," "cdebconf=0.251ubuntu1",
61 "libext2fs2:amd64=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
62 "libfdisk1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
63 "libffi7:amd64=3.3-4,libffi=3.3-4",
64 "libgcc-s1:amd64=10.2.0-5ubuntu1~20.04," "gcc-10=10.2.0-5ubuntu1~20.04",
65 "libgcrypt20:amd64=1.8.5-5ubuntu1," "libgcrypt20=1.8.5-5ubuntu1",
66 "libgmp10:amd64=2:6.2.0+dfsg-4," "gmp=2:6.2.0+dfsg-4",
67 "libgnutls30:amd64=3.6.13-2ubuntu1.3," "gnutls28=3.6.13-2ubuntu1.3",
68 "libgpg-error0:amd64=1.37-1," "libgpg-error=1.37-1",
69 "libhiredis0.14:amd64=0.14.0-6," "hiredis=0.14.0-6",
70 "libhogweed5:amd64=3.5.1+really3.5.1-2," "nettle=3.5.1+really3.5.1-2",
71 "libidn2-0:amd64=2.2.0-2,libidn2=2.2.0-2",
72 "libjemalloc2:amd64=5.2.1-1ubuntu1," "jemalloc=5.2.1-1ubuntu1",
73 "liblua5.1-0:amd64=5.1.5-8.1build4," "lua5.1=5.1.5-8.1build4",
74 "liblz4-1:amd64=1.9.2-2,lz4=1.9.2-2",
75 "liblzma5:amd64=5.2.4-1ubuntu1," "xz-utils=5.2.4-1ubuntu1",
76 "libmount1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
77 "libncurses6:amd64=6.2-0ubuntu2," "ncurses=6.2-0ubuntu2",
78 "libncursesw6:amd64=6.2-0ubuntu2," "ncurses=6.2-0ubuntu2",
79 "libnettle7:amd64=3.5.1+really3.5.1-2," "nettle=3.5.1+really3.5.1-2",
80 "libp11-kit0:amd64=0.23.20-1build1," "p11-kit=0.23.20-1build1",
81 "libpam-modules:amd64=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
82 "libpam-modules-bin=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
83 "libpam-runtime=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
84 "libpam0g:amd64=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
85 "libpcre2-8-0:amd64=10.34-7,pcre2=10.34-7",
86 "libpcre3:amd64=2:8.39-12build1," "pcre3=2:8.39-12build1",
87 "libprocps8:amd64=2:3.3.16-1ubuntu2," "procps=2:3.3.16-1ubuntu2",
88 "libseccomp2:amd64=2.4.3-1ubuntu3.20.04.3,"
89 "libseccomp=2.4.3-1ubuntu3.20.04.3",
90 "libselinux1:amd64=3.0-1build2,libselinux=3.0-1build2",
91 "libsemanage-common=3.0-1build2,libsemanage=3.0-1build2",
92 "libsemanage1:amd64=3.0-1build2,libsemanage=3.0-1build2",
93 "libsepol1:amd64=3.0-1,libsepol=3.0-1",
94 "libsmartcols1:amd64=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
95 "libss2:amd64=1.45.5-2ubuntu1,e2fsprogs=1.45.5-2ubuntu1",
96 "libstdc++6:amd64=10.2.0-5ubuntu1~20.04,gcc-10=10.2.0-5ubuntu1~20.04",
97 "libsystemd0:amd64=245.4-4ubuntu3.3,systemd=245.4-4ubuntu3.3",
98 "libtasn1-6:amd64=4.16.0-2,libtasn1-6=4.16.0-2",
99 "libtinfo6:amd64=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
100 "libudev1:amd64=245.4-4ubuntu3.3,systemd=245.4-4ubuntu3.3",
101 "libunistring2:amd64=0.9.10-2,libunistring=0.9.10-2",
102 "libuuid1:amd64=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
103 "libxcursor1=4.0.0-2,tiff=4.0.0-2",
104 "libzstd1:amd64=1.4.4+dfsg-3,libzstd=1.4.4+dfsg-3",
105 "login=1:4.8.1-1ubuntu5.20.04,shadow=1:4.8.1-1ubuntu5.20.04",
106 "logsave=1.45.5-2ubuntu1,e2fsprogs=1.45.5-2ubuntu1",
107 "lsb-base=11.1.0ubuntu2,lsb=11.1.0ubuntu2",
108 "lua-bitop:amd64=1.0.2-5,lua-bitop=1.0.2-5",
109 "lua-cjson:amd64=2.1.0+dfsg-2.1,lua-cjson=2.1.0+dfsg-2.1",
110 "mawk=1.3.4.20200120-2,mawk=1.3.4.20200120-2",
111 "mount=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
112 "ncurses-base=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
113 "ncurses-bin=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
114 "passwd=1:4.8.1-1ubuntu5.20.04,shadow=1:4.8.1-1ubuntu5.20.04",
115 "perl-base=5.30.0-9ubuntu0.2,perl=5.30.0-9ubuntu0.2",
116 "procps=2:3.3.16-1ubuntu2,procps=2:3.3.16-1ubuntu2",
117 "pwgen=2.08-2,pwgen=2.08-2",
118 "redis-server=5:5.0.7-2,redis=5:5.0.7-2",
119 "redis-tools=5:5.0.7-2,redis=5:5.0.7-2",
120 "sed=4.7-1,sed=4.7-1",
121 "sensible-utils=0.0.12+nmu1,sensible-utils=0.0.12+nmu1",
122 "sysvinit-utils=2.96-2.1ubuntu1,sysvinit=2.96-2.1ubuntu1",
123 "tar=1.30+dfsg-7,tar=1.30+dfsg-7",
124 "tzdata=2020d-0ubuntu0.20.04,tzdata=2020d-0ubuntu0.20.04",
125 "ubuntu-keyring=2020.02.11.2,ubuntu-keyring=2020.02.11.2",
126 "util-linux=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
127 "zlib1g:amd64=1:1.2.11.dfsg-2ubuntu1.2,zlib=1:1.2.11.dfsg-2ubuntu1.2",
128 ],
129 }
130 valid_rock = RockContainer(valid_rock_fn)
131 self.assertDictEqual(
132 expected_manifest_yaml, valid_rock.get_rock_manifest()
133 )
134
135 def test_get_rock_manifest_invalid_rock(self):
136 """Test get_rock_manifest() - invalid rock tar"""
137 invalid_rock_fn = "./tests/invalid_rock_multiple_layer_tar_archives.tar"
138 invalid_rock = RockContainer(invalid_rock_fn)
139 with self.assertRaises(ReviewException) as e:
140 invalid_rock.get_rock_manifest()
141 self.assertEqual(
142 e.exception.value,
143 "Unexpected number of layer tar archives inside layer directory: 2",
144 )
diff --git a/reviewtools/tests/containers/test_tar_container.py b/reviewtools/tests/containers/test_tar_container.py
0new file mode 100644145new file mode 100644
index 0000000..153ef3f
--- /dev/null
+++ b/reviewtools/tests/containers/test_tar_container.py
@@ -0,0 +1,70 @@
1import os
2import unittest
3from tempfile import mkstemp, mkdtemp
4from reviewtools.containers.tar_container import (
5 TarContainer,
6 ContainerException,
7)
8
9
10class TestTarContainer(unittest.TestCase):
11 """Tests for reviewtools.containers.tar_container"""
12
13 @classmethod
14 def setUpClass(cls):
15 cls.tmp_dir = mkdtemp()
16 cls.fn = "./tests/test-rock-redis_5.0-20.04.tar"
17
18 # Test initialization
19 def test_check_initialization__happy(self):
20 TarContainer(self.fn)
21
22 def test_check_initialization__invalid_format(self):
23 fd, plain_file = mkstemp(suffix=".tar")
24 with self.assertRaises(ContainerException) as context:
25 TarContainer(plain_file)
26 self.assertEqual(
27 "Unsupported package format (not tar)", context.exception.value
28 )
29 os.unlink(plain_file)
30
31 # Test calculate unpacked size
32 def test_calculate_unpacked_size__happy(self):
33 # unpacked size calculated is a bit smaller than actual size as it does not consider that
34 # folders require at least one complete block
35 container = TarContainer(self.fn)
36 size = container.calculate_unpacked_size()
37 self.assertTrue(isinstance(size, int))
38
39 # Test unpack
40 def test_unpack__happy(self):
41 container = TarContainer(self.fn)
42 container.unpack()
43
44 def test_unpack__force(self):
45 container = TarContainer(self.fn)
46 container.unpack()
47 container.unpack(force=True)
48
49 def test_unpack__no_force(self):
50 container = TarContainer(self.fn)
51 container.unpack()
52 with self.assertRaises(ContainerException) as context:
53 container.unpack()
54 self.assertTrue(" exists. Aborting." in context.exception.value)
55
56 def test_check_unpack__slash_file(self):
57 container = TarContainer("./tests/test-rock-invalid-1.tar")
58 with self.assertRaises(ContainerException) as context:
59 container.unpack()
60 self.assertTrue(
61 " while extracting archive at " in context.exception.value
62 )
63
64 def test_check_unpack__two_dots_file(self):
65 container = TarContainer("./tests/test-rock-invalid-2.tar")
66 with self.assertRaises(ContainerException) as context:
67 container.unpack()
68 self.assertTrue(
69 " while extracting archive at " in context.exception.value
70 )
diff --git a/reviewtools/tests/test_common.py b/reviewtools/tests/test_common.py
index 85504ca..953d2c0 100644
--- a/reviewtools/tests/test_common.py
+++ b/reviewtools/tests/test_common.py
@@ -19,7 +19,6 @@ import os
19import shutil19import shutil
20import tempfile20import tempfile
2121
22from reviewtools.sr_common import ReviewException
23import reviewtools.sr_tests as sr_tests22import reviewtools.sr_tests as sr_tests
24import reviewtools.common23import reviewtools.common
25from reviewtools.common import StatLLN24from reviewtools.common import StatLLN
@@ -502,194 +501,3 @@ drwxr-xr-x 0/0 48 2020-03-24 09:11 squashfs-root/meta
502 continue501 continue
503502
504 raise Exception("parsed input should be invalid")503 raise Exception("parsed input should be invalid")
505
506 def test_check_pkg_uncompressed_size_ok(self):
507 """Test check_pkg_uncompressed_size() - rocks and snaps"""
508 pkgs = [
509 "./tests/test-rock-redis_5.0-20.04.tar",
510 "./tests/test-snapcraft-manifest-unittest_0_amd64.snap",
511 ]
512 max_size = 3 * 1024 * 1024
513 size = 1 * 1024 * 1024
514 for pkg in pkgs:
515 with self.subTest(pkg=pkg):
516 valid_size, _ = reviewtools.common.is_pkg_uncompressed_size_valid(
517 max_size, size, pkg
518 )
519 self.assertTrue(valid_size)
520
521 def test_check_pkg_uncompressed_size_max_size_error(self):
522 """Test check_pkg_uncompressed_size() - rocks and snaps"""
523 pkgs = [
524 "./tests/test-rock-redis_5.0-20.04.tar",
525 "./tests/test-snapcraft-manifest-unittest_0_amd64.snap",
526 ]
527 max_size = 1024
528 size = 1 * 1024 * 1024
529 for pkg in pkgs:
530 with self.subTest(pkg=pkg):
531 valid_size, _ = reviewtools.common.is_pkg_uncompressed_size_valid(
532 max_size, size, pkg
533 )
534 self.assertFalse(valid_size)
535
536 def test_unpack_rock_invalid_format(self):
537 """Test unpack_rock() - invalid rock format"""
538 invalid_rock = "./tests/test-snapcraft-manifest-unittest_0_amd64.snap"
539 with self.assertRaises(SystemExit) as e:
540 reviewtools.common.unpack_rock(invalid_rock)
541 self.assertEqual(e.exception.code, 1)
542
543 def test_unpack_rock_valid_format(self):
544 """Test unpack_rock() - valid rock format"""
545 # TODO: add further unit testing for tar unpacking functionality
546 valid_rock = "./tests/test-rock-redis_5.0-20.04.tar"
547 unpack_dir = reviewtools.common.unpack_rock(valid_rock)
548 self.assertIn("review-tools-", unpack_dir)
549
550 def test_unpack_rock_invalid_format_filename_starting_with_slash(self):
551 """Test unpack_rock() - invalid - filename starting with slash"""
552 # TODO: add further unit testing for tar unpacking functionality
553 invalid_rock = "./tests/test-rock-invalid-1.tar"
554 with self.assertRaises(SystemExit) as e:
555 reviewtools.common.unpack_rock(invalid_rock)
556 self.assertEqual(e.exception.code, 1)
557
558 def test_unpack_rock_invalid_format_filename_with_two_dots(self):
559 """Test unpack_rock() - invalid - filename with two dots"""
560 # TODO: add further unit testing for tar unpacking functionality
561 invalid_rock = "./tests/test-rock-invalid-2.tar"
562 with self.assertRaises(SystemExit) as e:
563 reviewtools.common.unpack_rock(invalid_rock)
564 self.assertEqual(e.exception.code, 1)
565
566 def test_get_rock_manifest(self):
567 """Test get_rock_manifest()"""
568 valid_rock_fn = "./tests/test-rock-redis_5.0-20.04.tar"
569 expected_manifest_yaml = {
570 "manifest-version": "1",
571 "os-release-id": "ubuntu",
572 "os-release-version-id": "20.04",
573 "stage-packages": [
574 "adduser=3.118ubuntu2,adduser=3.118ubuntu2",
575 "apt=2.0.2ubuntu0.2,apt=2.0.2ubuntu0.2",
576 "base-files=11ubuntu5.2,base-files=11ubuntu5.2",
577 "base-passwd=3.5.47,base-passwd=3.5.47",
578 "bash=5.0-6ubuntu1.1,bash=5.0-6ubuntu1.1",
579 "bsdutils=1:2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
580 "bzip2=1.0.8-2,bzip2=1.0.8-2",
581 "coreutils=8.30-3ubuntu2," "coreutils=8.30-3ubuntu2",
582 "dash=0.5.10.2-6,dash=0.5.10.2-6",
583 "debconf=1.5.73,debconf=1.5.73",
584 "debianutils=4.9.1,debianutils=4.9.1",
585 "diffutils=1:3.7-3,diffutils=1:3.7-3",
586 "dpkg=1.19.7ubuntu3,dpkg=1.19.7ubuntu3",
587 "e2fsprogs=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
588 "fdisk=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
589 "findutils=4.7.0-1ubuntu1," "findutils=4.7.0-1ubuntu1",
590 "gcc-10-base:amd64=10.2.0-5ubuntu1~20.04,"
591 "gcc-10=10.2.0-5ubuntu1~20.04",
592 "gpgv=2.2.19-3ubuntu2,gnupg2=2.2.19-3ubuntu2",
593 "grep=3.4-1,grep=3.4-1",
594 "gzip=1.10-0ubuntu4,gzip=1.10-0ubuntu4",
595 "hostname=3.23,hostname=3.23",
596 "init-system-helpers=1.57," "init-system-helpers=1.57",
597 "libacl1:amd64=2.2.53-6,acl=2.2.53-6",
598 "libapt-pkg6.0:amd64=2.0.2ubuntu0.2," "apt=2.0.2ubuntu0.2",
599 "libatomic1:amd64=10.2.0-5ubuntu1~20.04,"
600 "gcc-10=10.2.0-5ubuntu1~20.04",
601 "libattr1:amd64=1:2.4.48-5,attr=1:2.4.48-5",
602 "libaudit-common=1:2.8.5-2ubuntu6," "audit=1:2.8.5-2ubuntu6",
603 "libaudit1:amd64=1:2.8.5-2ubuntu6," "audit=1:2.8.5-2ubuntu6",
604 "libblkid1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
605 "libbz2-1.0:amd64=1.0.8-2,bzip2=1.0.8-2",
606 "libc-bin=2.31-0ubuntu9.1," "glibc=2.31-0ubuntu9.1",
607 "libc6:amd64=2.31-0ubuntu9.1," "glibc=2.31-0ubuntu9.1",
608 "libcap-ng0:amd64=0.7.9-2.1build1," "libcap-ng=0.7.9-2.1build1",
609 "libcom-err2:amd64=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
610 "libcrypt1:amd64=1:4.4.10-10ubuntu4," "libxcrypt=1:4.4.10-10ubuntu4",
611 "libdb5.3:amd64=5.3.28+dfsg1-0.6ubuntu2,"
612 "db5.3=5.3.28+dfsg1-0.6ubuntu2",
613 "libdebconfclient0:amd64=0.251ubuntu1," "cdebconf=0.251ubuntu1",
614 "libext2fs2:amd64=1.45.5-2ubuntu1," "e2fsprogs=1.45.5-2ubuntu1",
615 "libfdisk1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
616 "libffi7:amd64=3.3-4,libffi=3.3-4",
617 "libgcc-s1:amd64=10.2.0-5ubuntu1~20.04," "gcc-10=10.2.0-5ubuntu1~20.04",
618 "libgcrypt20:amd64=1.8.5-5ubuntu1," "libgcrypt20=1.8.5-5ubuntu1",
619 "libgmp10:amd64=2:6.2.0+dfsg-4," "gmp=2:6.2.0+dfsg-4",
620 "libgnutls30:amd64=3.6.13-2ubuntu1.3," "gnutls28=3.6.13-2ubuntu1.3",
621 "libgpg-error0:amd64=1.37-1," "libgpg-error=1.37-1",
622 "libhiredis0.14:amd64=0.14.0-6," "hiredis=0.14.0-6",
623 "libhogweed5:amd64=3.5.1+really3.5.1-2," "nettle=3.5.1+really3.5.1-2",
624 "libidn2-0:amd64=2.2.0-2,libidn2=2.2.0-2",
625 "libjemalloc2:amd64=5.2.1-1ubuntu1," "jemalloc=5.2.1-1ubuntu1",
626 "liblua5.1-0:amd64=5.1.5-8.1build4," "lua5.1=5.1.5-8.1build4",
627 "liblz4-1:amd64=1.9.2-2,lz4=1.9.2-2",
628 "liblzma5:amd64=5.2.4-1ubuntu1," "xz-utils=5.2.4-1ubuntu1",
629 "libmount1:amd64=2.34-0.1ubuntu9.1," "util-linux=2.34-0.1ubuntu9.1",
630 "libncurses6:amd64=6.2-0ubuntu2," "ncurses=6.2-0ubuntu2",
631 "libncursesw6:amd64=6.2-0ubuntu2," "ncurses=6.2-0ubuntu2",
632 "libnettle7:amd64=3.5.1+really3.5.1-2," "nettle=3.5.1+really3.5.1-2",
633 "libp11-kit0:amd64=0.23.20-1build1," "p11-kit=0.23.20-1build1",
634 "libpam-modules:amd64=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
635 "libpam-modules-bin=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
636 "libpam-runtime=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
637 "libpam0g:amd64=1.3.1-5ubuntu4.1," "pam=1.3.1-5ubuntu4.1",
638 "libpcre2-8-0:amd64=10.34-7,pcre2=10.34-7",
639 "libpcre3:amd64=2:8.39-12build1," "pcre3=2:8.39-12build1",
640 "libprocps8:amd64=2:3.3.16-1ubuntu2," "procps=2:3.3.16-1ubuntu2",
641 "libseccomp2:amd64=2.4.3-1ubuntu3.20.04.3,"
642 "libseccomp=2.4.3-1ubuntu3.20.04.3",
643 "libselinux1:amd64=3.0-1build2,libselinux=3.0-1build2",
644 "libsemanage-common=3.0-1build2,libsemanage=3.0-1build2",
645 "libsemanage1:amd64=3.0-1build2,libsemanage=3.0-1build2",
646 "libsepol1:amd64=3.0-1,libsepol=3.0-1",
647 "libsmartcols1:amd64=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
648 "libss2:amd64=1.45.5-2ubuntu1,e2fsprogs=1.45.5-2ubuntu1",
649 "libstdc++6:amd64=10.2.0-5ubuntu1~20.04,gcc-10=10.2.0-5ubuntu1~20.04",
650 "libsystemd0:amd64=245.4-4ubuntu3.3,systemd=245.4-4ubuntu3.3",
651 "libtasn1-6:amd64=4.16.0-2,libtasn1-6=4.16.0-2",
652 "libtinfo6:amd64=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
653 "libudev1:amd64=245.4-4ubuntu3.3,systemd=245.4-4ubuntu3.3",
654 "libunistring2:amd64=0.9.10-2,libunistring=0.9.10-2",
655 "libuuid1:amd64=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
656 "libxcursor1=4.0.0-2,tiff=4.0.0-2",
657 "libzstd1:amd64=1.4.4+dfsg-3,libzstd=1.4.4+dfsg-3",
658 "login=1:4.8.1-1ubuntu5.20.04,shadow=1:4.8.1-1ubuntu5.20.04",
659 "logsave=1.45.5-2ubuntu1,e2fsprogs=1.45.5-2ubuntu1",
660 "lsb-base=11.1.0ubuntu2,lsb=11.1.0ubuntu2",
661 "lua-bitop:amd64=1.0.2-5,lua-bitop=1.0.2-5",
662 "lua-cjson:amd64=2.1.0+dfsg-2.1,lua-cjson=2.1.0+dfsg-2.1",
663 "mawk=1.3.4.20200120-2,mawk=1.3.4.20200120-2",
664 "mount=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
665 "ncurses-base=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
666 "ncurses-bin=6.2-0ubuntu2,ncurses=6.2-0ubuntu2",
667 "passwd=1:4.8.1-1ubuntu5.20.04,shadow=1:4.8.1-1ubuntu5.20.04",
668 "perl-base=5.30.0-9ubuntu0.2,perl=5.30.0-9ubuntu0.2",
669 "procps=2:3.3.16-1ubuntu2,procps=2:3.3.16-1ubuntu2",
670 "pwgen=2.08-2,pwgen=2.08-2",
671 "redis-server=5:5.0.7-2,redis=5:5.0.7-2",
672 "redis-tools=5:5.0.7-2,redis=5:5.0.7-2",
673 "sed=4.7-1,sed=4.7-1",
674 "sensible-utils=0.0.12+nmu1,sensible-utils=0.0.12+nmu1",
675 "sysvinit-utils=2.96-2.1ubuntu1,sysvinit=2.96-2.1ubuntu1",
676 "tar=1.30+dfsg-7,tar=1.30+dfsg-7",
677 "tzdata=2020d-0ubuntu0.20.04,tzdata=2020d-0ubuntu0.20.04",
678 "ubuntu-keyring=2020.02.11.2,ubuntu-keyring=2020.02.11.2",
679 "util-linux=2.34-0.1ubuntu9.1,util-linux=2.34-0.1ubuntu9.1",
680 "zlib1g:amd64=1:1.2.11.dfsg-2ubuntu1.2,zlib=1:1.2.11.dfsg-2ubuntu1.2",
681 ],
682 }
683 self.assertDictEqual(
684 expected_manifest_yaml, reviewtools.common.get_rock_manifest(valid_rock_fn)
685 )
686
687 def test_get_rock_manifest_invalid_rock(self):
688 """Test get_rock_manifest() - invalid rock tar"""
689 invalid_rock_fn = "./tests/invalid_rock_multiple_layer_tar_archives.tar"
690 with self.assertRaises(ReviewException) as e:
691 reviewtools.common.get_rock_manifest(invalid_rock_fn)
692 self.assertEqual(
693 e.exception.value,
694 "Unexpected number of layer tar archives inside layer directory: 2",
695 )

Subscribers

People subscribed via source and target branches