← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~pappacena/launchpad-buildd:revert-oci-security-manifest into launchpad-buildd:master

 

Thiago F. Pappacena has proposed merging ~pappacena/launchpad-buildd:revert-oci-security-manifest into launchpad-buildd:master.

Commit message:
Reverting OCI security manifest implementation

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/launchpad-buildd/+git/launchpad-buildd/+merge/394110

The patch was generated by running the following git revert command: git revert --no-commit -m 1 54b2539f81e5d91f71ce32ad11ee08d04a76eebe be12be72f1425cf731437f8f7063676fb6fac427 a7b09d814e7c8a60393cbbf20ae3f7c59cac3d3d
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad-buildd:revert-oci-security-manifest into launchpad-buildd:master.
diff --git a/lpbuildd/oci.py b/lpbuildd/oci.py
index 5904b55..6a404fd 100644
--- a/lpbuildd/oci.py
+++ b/lpbuildd/oci.py
@@ -53,7 +53,6 @@ class OCIBuildManager(SnapBuildProxyMixin, DebianBuildManager):
         self.build_path = extra_args.get("build_path")
         self.proxy_url = extra_args.get("proxy_url")
         self.revocation_endpoint = extra_args.get("revocation_endpoint")
-        self.metadata = extra_args.get("metadata")
         self.proxy_service = None
 
         super(OCIBuildManager, self).initiate(files, chroot, extra_args)
@@ -83,11 +82,6 @@ class OCIBuildManager(SnapBuildProxyMixin, DebianBuildManager):
             args.extend(["--snap-store-proxy-url", snap_store_proxy_url])
         except (NoSectionError, NoOptionError):
             pass
-        if self.metadata is not None:
-            try:
-                args.extend(["--metadata", json.dumps(self.metadata)])
-            except TypeError as e:
-                print("Could not JSONify metadata: %s" % e)
         args.append(self.name)
         self.runTargetSubProcess("build-oci", *args)
 
diff --git a/lpbuildd/target/build_oci.py b/lpbuildd/target/build_oci.py
index 8370c56..af56671 100644
--- a/lpbuildd/target/build_oci.py
+++ b/lpbuildd/target/build_oci.py
@@ -6,16 +6,12 @@ from __future__ import print_function
 __metaclass__ = type
 
 from collections import OrderedDict
-import json
 import logging
 import os.path
-import re
 import sys
 import tempfile
 from textwrap import dedent
 
-from debian.deb822 import Deb822
-
 from lpbuildd.target.operation import Operation
 from lpbuildd.target.snapbuildproxy import SnapBuildProxyOperationMixin
 from lpbuildd.target.snapstore import SnapStoreOperationMixin
@@ -51,19 +47,12 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
             help="A docker build ARG in the format of key=value. "
                  "This option can be repeated many times. For example: "
                  "--build-arg VAR1=A --build-arg VAR2=B")
-        parser.add_argument(
-            "--metadata", default=None,
-            help="Metadata about this build, used to generate manifest file.")
         parser.add_argument("name", help="name of snap to build")
 
     def __init__(self, args, parser):
         super(BuildOCI, self).__init__(args, parser)
         self.bin = os.path.dirname(sys.argv[0])
         self.buildd_path = os.path.join("/home/buildd", self.args.name)
-        # Temp directory where we store files that will be included in the
-        # final filesystem of the image.
-        self.backend_tmp_fs_dir = "/tmp/image-root-dir/"
-        self.security_manifest_target_path = "/.rocks/manifest.json"
 
     def _add_docker_engine_proxy_settings(self):
         """Add systemd file for docker proxy settings."""
@@ -130,167 +119,6 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
         env = self.build_proxy_environment(proxy_url=self.args.proxy_url)
         self.vcs_fetch(self.args.name, cwd="/home/buildd", env=env)
 
-    def _getCurrentVCSRevision(self):
-        if self.args.branch is not None:
-            revision_cmd = ["bzr", "revno"]
-        else:
-            revision_cmd = ["git", "rev-parse", "HEAD"]
-        return self.backend.run(
-            revision_cmd, cwd=os.path.join("/home/buildd", self.args.name),
-            get_output=True).decode("UTF-8", "replace").strip()
-
-    def _getContainerPackageList(self):
-        """Extracts package list from /var/lib/dpkg/status.
-
-        :return: A list of dict with package information, in the format
-            {"package": "xx", "version": "yy", "source": "zz"}.
-        """
-        tmp_file = "/tmp/dpkg-status"
-        self.run_build_command([
-            "docker", "cp", "-L",
-            "%s:/var/lib/dpkg/status" % self.args.name, tmp_file])
-        content = self.backend.run(["cat", tmp_file], get_output=True)
-
-        packages = []
-        for paragraph in Deb822.iter_paragraphs(content.split(b"\n")):
-            if paragraph.get("Status") != "install ok installed":
-                continue
-            keys = ["Package", "Version", "Source"]
-            pkg = {i.lower(): paragraph.get(i) for i in keys}
-            if not pkg.get('source'):
-                pkg['source'] = pkg['package']
-            packages.append(pkg)
-        return packages
-
-    def _getContainerOSRelease(self):
-        tmp_file = "/tmp/os-release"
-        self.run_build_command([
-            "docker", "cp",  "-L",
-            "%s:/etc/os-release" % self.args.name, tmp_file])
-        content = self.backend.run(["cat", tmp_file], get_output=True)
-        os_release = {}
-        # Variable content might be enclosed by double-quote, single-quote
-        # or no quote at all. We accept everything.
-        content_expr = re.compile(r"""^"(.*)"$|^'(.*)'$|^(.*)$""")
-        unquote = lambda string: [
-            i for i in content_expr.match(string).groups() if i is not None][0]
-        for line in content.decode("UTF-8", "replace").split("\n"):
-            if '=' not in line:
-                continue
-            key, value = line.strip().split("=", 1)
-            os_release[key] = unquote(value)
-        return os_release
-
-    def _getSecurityManifestContent(self):
-        try:
-            metadata = json.loads(self.args.metadata) or {}
-        except TypeError:
-            logger.warning(
-                "No valid OCI build metadata received. Expecting a valid "
-                "JSON, but got %s." % self.args.metadata)
-            metadata = {}
-        recipe_owner = metadata.get("recipe_owner", {})
-        build_requester = metadata.get("build_requester", {})
-        emails = [i.get("email") for i in (recipe_owner, build_requester)
-                  if i.get("email")]
-
-        try:
-            packages = self._getContainerPackageList()
-        except Exception as e:
-            logger.warning("Failed to get container package list: %s", e)
-            packages = []
-        try:
-            vcs_current_version = self._getCurrentVCSRevision()
-        except Exception as e:
-            logger.warning("Failed to get current VCS revision: %s" % e)
-            vcs_current_version = None
-        try:
-            os_release = self._getContainerOSRelease()
-        except Exception as e:
-            logger.warning("Failed to get /etc/os-release info: %s" % e)
-            os_release = {}
-
-        return {
-            "manifest-version": "1",
-            "name": self.args.name,
-            "os-release-id": os_release.get("ID"),
-            "os-release-version-id": os_release.get("VERSION_ID"),
-            "architectures": metadata.get("architectures") or [self.args.arch],
-            "publisher-emails": emails,
-            "image-info": {
-                "build-request-id": metadata.get("build_request_id"),
-                "build-request-timestamp": metadata.get(
-                    "build_request_timestamp"),
-                "build-urls": metadata.get("build_urls") or {}
-            },
-            "vcs-info": [{
-                "source": self.args.git_repository,
-                "source-branch": self.args.git_path,
-                "source-commit": vcs_current_version,
-                "source-subdir": self.args.build_path,
-                "source-build-file": self.args.build_file,
-                "source-build-args": self.args.build_arg
-            }],
-            "packages": packages
-        }
-
-    def createSecurityManifest(self):
-        """Generates the security manifest file, returning the tmp file name
-        where it is stored in the backend.
-        """
-        content = self._getSecurityManifestContent()
-        local_filename = tempfile.mktemp()
-        destination_path = self.security_manifest_target_path.lstrip(
-            os.path.sep)
-        destination = os.path.join(self.backend_tmp_fs_dir, destination_path)
-        logger.info("Security manifest: %s" % content)
-        with open(local_filename, 'w') as fd:
-            json.dump(content, fd, indent=2)
-        self.backend.copy_in(local_filename, destination)
-        return destination
-
-    def initTempRootDir(self):
-        """Initialize in the backend the directories that will be included in
-        resulting image's filesystem.
-        """
-        security_manifest_dir = os.path.dirname(
-            self.security_manifest_target_path)
-        dir = os.path.join(
-            self.backend_tmp_fs_dir,
-            security_manifest_dir.lstrip(os.path.sep))
-        self.backend.run(["mkdir", "-p", dir])
-
-    def createImageContainer(self):
-        """Creates a container from the built image, so we can play with
-        it's filesystem."""
-        self.run_build_command([
-            "docker", "create", "--name", self.args.name, self.args.name])
-
-    def removeImageContainer(self):
-        self.run_build_command(["docker", "rm", self.args.name])
-
-    def commitImage(self):
-        """Commits the tmp container, overriding the originally built image."""
-        self.run_build_command([
-            "docker", "commit", self.args.name, self.args.name])
-
-    def addFilesToImageContainer(self):
-        """Flushes all files from temp root dir (in the backend) to the
-        resulting image container."""
-        # The extra '.' in the end is important. It indicates to docker that
-        # the directory itself should be copied, instead of the list of
-        # files in the directory. It makes docker keep the paths.
-        src = os.path.join(self.backend_tmp_fs_dir, ".")
-        self.run_build_command(["docker", "cp", src, "%s:/" % self.args.name])
-
-    def addSecurityManifest(self):
-        self.createImageContainer()
-        self.initTempRootDir()
-        self.createSecurityManifest()
-        self.addFilesToImageContainer()
-        self.commitImage()
-        self.removeImageContainer()
-
     def build(self):
         logger.info("Running build phase...")
         args = ["docker", "build", "--no-cache"]
@@ -315,7 +143,6 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
         self._check_path_escape(build_context_path)
         args.append(build_context_path)
         self.run_build_command(args)
-        self.addSecurityManifest()
 
     def run(self):
         try:
diff --git a/lpbuildd/target/tests/test_build_oci.py b/lpbuildd/target/tests/test_build_oci.py
index 9bb9343..e58344f 100644
--- a/lpbuildd/target/tests/test_build_oci.py
+++ b/lpbuildd/target/tests/test_build_oci.py
@@ -3,16 +3,9 @@
 
 __metaclass__ = type
 
-import datetime
-import json
-try:
-    from unittest import mock
-except ImportError:
-    import mock
 import os.path
 import stat
 import subprocess
-import tempfile
 from textwrap import dedent
 
 from fixtures import (
@@ -80,217 +73,6 @@ class RanBuildCommand(RanCommand):
         super(RanBuildCommand, self).__init__(args, **kwargs)
 
 
-class TestBuildOCIManifestGeneration(TestCase):
-    def test_getSecurityManifestContent(self):
-        now = datetime.datetime.now().isoformat()
-        metadata = {
-            "architectures": ["amd64", "386"],
-            "recipe_owner": dict(name="pappacena", email="me@xxxxxxx"),
-            "build_request_id": 123,
-            "build_request_timestamp": now,
-            "build_requester": dict(name="someone", email="someone@xxxxxxx"),
-            "build_urls": {
-                "amd64": "http://lp.net/build/1";,
-                "386": "http://lp.net/build/2";
-            },
-        }
-        args = [
-            "build-oci",
-            "--backend=fake", "--series=xenial", "--arch=amd64", "1",
-            "--git-repository", "lp:git-repo", "--git-path", "refs/heads/main",
-            "--build-arg", "VAR1=1", "--build-arg", "VAR2=22",
-            "--build-file", "SomeDockerfile", "--build-path", "docker/builder",
-            "--metadata", json.dumps(metadata),
-            "test-image"
-        ]
-        build_oci = parse_args(args=args).operation
-
-        # Expected build_oci.backend.run outputs.
-        commit_hash = b"a1b2c3d4e5f5"
-        dpkg_status_content = dedent("""
-            Package: adduser
-            Version: 3.118
-            Status: install ok installed
-
-            Package: apt
-            Version: 1.8.2.1
-            Status: install ok installed
-
-            Package: util-linux
-            Version: 2.33.1
-            Status: install ok installed
-
-            Package: zlib1g
-            Version: 1:1.2.11
-            Source: zlib
-            Status: install ok installed
-
-            Package: to-be-ignored
-            Version: 111
-            Status: invalid
-            """).encode('utf8')
-
-        os_release_cat_output = dedent("""
-        NAME="Ubuntu"
-        VERSION="20.04.1 LTS (Focal Fossa)"
-        ID=ubuntu
-        ID_LIKE=debian
-        PRETTY_NAME="Ubuntu 20.04.1 LTS"
-        VERSION_ID="20.04"
-        HOME_URL="https://www.ubuntu.com/";
-        SUPPORT_URL="https://help.ubuntu.com/";
-        BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/";
-        PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy";
-        VERSION_CODENAME=focal
-        UBUNTU_CODENAME=focal
-        """).encode("utf8")
-
-        # Side effect for "docker cp...", "git rev-parse...", ...
-        build_oci.backend.run = mock.Mock(side_effect=[
-            # docker cp, cat to get packages file.
-            None, dpkg_status_content,
-            # git rev-parse HEAD to get current revision.
-            commit_hash,
-            # docker cp and cat for container /etc/os-release.
-            None, os_release_cat_output])
-
-        self.assertEqual(build_oci._getSecurityManifestContent(), {
-            "manifest-version": "1",
-            "name": "test-image",
-            'os-release-id': "ubuntu",
-            'os-release-version-id': "20.04",
-            "architectures": ["amd64", "386"],
-            "publisher-emails": ["me@xxxxxxx", "someone@xxxxxxx"],
-            "image-info": {
-                "build-request-id": 123,
-                "build-request-timestamp": now,
-                "build-urls": {
-                    "386": "http://lp.net/build/2";,
-                    "amd64": "http://lp.net/build/1"}},
-            "vcs-info": [{
-                "source": "lp:git-repo",
-                "source-branch": "refs/heads/main",
-                "source-build-args": ["VAR1=1", "VAR2=22"],
-                "source-build-file": "SomeDockerfile",
-                "source-commit": "a1b2c3d4e5f5",
-                "source-subdir": "docker/builder"
-            }],
-            "packages": [
-                {'package': 'adduser', 'source': 'adduser',
-                 'version': '3.118'},
-                {'package': 'apt', 'source': 'apt',
-                 'version': '1.8.2.1'},
-                {'package': 'util-linux', 'source': 'util-linux',
-                 'version': '2.33.1'},
-                {'package': 'zlib1g', 'source': 'zlib',
-                 'version': '1:1.2.11'}
-            ]
-        })
-
-    def test_getSecurityManifestContent_without_manifest(self):
-        """With minimal parameters, the manifest content should give
-        something back without breaking."""
-        args = [
-            "build-oci",
-            "--backend=fake", "--series=xenial", "--arch=amd64", "1",
-            "--git-repository", "lp:git-repo", "--git-path", "refs/heads/main",
-            "test-image"
-        ]
-
-        # Here we will not mock the package gathering nor os-release file
-        # reading in order to let it raise exception, so we end up with a
-        # manifest without packages.
-        build_oci = parse_args(args=args).operation
-
-        self.assertEqual(build_oci._getSecurityManifestContent(), {
-            "manifest-version": "1",
-            "name": "test-image",
-            'os-release-id': None,
-            'os-release-version-id': None,
-            "architectures": ["amd64"],
-            "publisher-emails": [],
-            "image-info": {
-                "build-request-id": None,
-                "build-request-timestamp": None,
-                "build-urls": {}},
-            "vcs-info": [{
-                "source": "lp:git-repo",
-                "source-branch": "refs/heads/main",
-                "source-build-args": [],
-                "source-build-file": None,
-                "source-commit": None,
-                "source-subdir": "."
-            }],
-            "packages": []
-        })
-
-    def test_getContainerPackageList(self):
-        args = [
-            "build-oci",
-            "--backend=fake", "--series=xenial", "--arch=amd64", "1",
-            "--git-repository", "lp:git-repo", "--git-path", "refs/heads/main",
-            "test-image"
-        ]
-        build_oci = parse_args(args=args).operation
-
-        dpkg_status_content = dedent("""
-            Package: adduser
-            Version: 3.118
-            Status: install ok installed
-
-            Package: apt
-            Version: 1.8.2.1
-            Status: install ok installed
-
-            Package: util-linux
-            Version: 2.33.1-0.1
-            Status: install ok installed
-
-            Package: zlib1g
-            Version: 1:1.2.11
-            Source: zlib
-            Status: install ok installed
-
-            Package invalid
-            Version: 000
-            Status: broken
-            """).encode("utf8")
-        build_oci.backend.run = mock.Mock(side_effect=[
-            # docker cp, cat to get packages file.
-            None, dpkg_status_content])
-        self.assertEqual([
-            {'package': 'adduser', 'source': 'adduser',
-             'version': '3.118'},
-            {'package': 'apt', 'source': 'apt',
-             'version': '1.8.2.1'},
-            {'package': 'util-linux', 'source': 'util-linux',
-             'version': '2.33.1-0.1'},
-            {'package': 'zlib1g', 'source': 'zlib',
-             'version': '1:1.2.11'}
-        ], build_oci._getContainerPackageList())
-
-    def test_getContainerOSRelease_quoting(self):
-        args = [
-            "build-oci",
-            "--backend=fake", "--series=xenial", "--arch=amd64", "1",
-            "--git-repository", "lp:git-repo", "--git-path", "refs/heads/main",
-            "test-image"
-        ]
-        os_release_cat_output = dedent("""
-            NO_QUOTE=ubuntu
-            DOUBLE_QUOTE="20.04"
-            SINGLE_QUOTE='Canonical'   
-        """).encode("utf8")
-        build_oci = parse_args(args=args).operation
-        build_oci.backend.run = mock.Mock(side_effect=[
-            # docker cp /etc/os-release /tmp-file; cat /tmp-file
-            None, os_release_cat_output])
-        self.assertEqual({
-            "NO_QUOTE": "ubuntu",
-            "DOUBLE_QUOTE": "20.04",
-            "SINGLE_QUOTE": "Canonical"}, build_oci._getContainerOSRelease())
-
-
 class TestBuildOCI(TestCase):
 
     def test_run_build_command_no_env(self):
@@ -508,47 +290,6 @@ class TestBuildOCI(TestCase):
                 cwd="/home/buildd/test-image", **env),
             ]))
 
-    def assertRanPostBuildCommands(self, build_oci):
-        rev_num_args = (
-            ['bzr', 'revno'] if build_oci.args.branch
-            else ['git', 'rev-parse', 'HEAD'])
-        self.assertThat(build_oci.backend.run.calls[1:], MatchesListwise([
-            RanBuildCommand(
-                ['docker', 'create', '--name', 'test-image', 'test-image'],
-                cwd="/home/buildd/test-image"),
-            RanCommand(['mkdir', '-p', '/tmp/image-root-dir/.rocks']),
-
-            # Manifest building: packages discovery.
-            RanBuildCommand([
-                'docker', 'cp', '-L',
-                'test-image:/var/lib/dpkg/status', '/tmp/dpkg-status'],
-                cwd="/home/buildd/test-image"),
-
-            RanCommand(['cat', '/tmp/dpkg-status'], get_output=True),
-
-            # Manifest building: get current revision number.
-            RanCommand(
-                rev_num_args, cwd="/home/buildd/test-image", get_output=True),
-
-            # Manifest building: os-release file.
-            RanBuildCommand([
-                'docker', 'cp',  '-L', 'test-image:/etc/os-release',
-                '/tmp/os-release'],
-                cwd="/home/buildd/test-image"),
-            RanCommand(['cat', '/tmp/os-release'], get_output=True),
-
-            # Filesystem injection and image commiting.
-            RanBuildCommand(
-                ['docker', 'cp', '/tmp/image-root-dir/.', 'test-image:/'],
-                cwd="/home/buildd/test-image"),
-            RanBuildCommand(
-                ['docker', 'commit', 'test-image', 'test-image'],
-                cwd="/home/buildd/test-image"),
-            RanBuildCommand(
-                ['docker', 'rm', 'test-image'],
-                cwd="/home/buildd/test-image"),
-        ]))
-
     def test_build(self):
         args = [
             "build-oci",
@@ -558,11 +299,12 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache", "--tag", "test-image",
-             "/home/buildd/test-image/."],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache", "--tag", "test-image",
+                 "/home/buildd/test-image/."],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_build_with_file(self):
         args = [
@@ -574,12 +316,13 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache", "--tag", "test-image",
-             "--file", "./build-aux/Dockerfile",
-             "/home/buildd/test-image/."],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache", "--tag", "test-image",
+                 "--file", "./build-aux/Dockerfile",
+                 "/home/buildd/test-image/."],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_build_with_path(self):
         args = [
@@ -591,11 +334,12 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache", "--tag", "test-image",
-             "/home/buildd/test-image/a-sub-directory/"],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache", "--tag", "test-image",
+                 "/home/buildd/test-image/a-sub-directory/"],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_build_with_file_and_path(self):
         args = [
@@ -608,12 +352,13 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache", "--tag", "test-image",
-             "--file", "test-build-path/build-aux/Dockerfile",
-             "/home/buildd/test-image/test-build-path"],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache", "--tag", "test-image",
+                 "--file", "test-build-path/build-aux/Dockerfile",
+                 "/home/buildd/test-image/test-build-path"],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_build_with_args(self):
         args = [
@@ -627,13 +372,14 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache", "--tag", "test-image",
-             "--file", "test-build-path/build-aux/Dockerfile",
-             "--build-arg=VAR1=xxx", "--build-arg=VAR2=yyy",
-             "/home/buildd/test-image/test-build-path"],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache", "--tag", "test-image",
+                 "--file", "test-build-path/build-aux/Dockerfile",
+                 "--build-arg=VAR1=xxx", "--build-arg=VAR2=yyy",
+                 "/home/buildd/test-image/test-build-path"],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_build_proxy(self):
         args = [
@@ -645,13 +391,14 @@ class TestBuildOCI(TestCase):
         build_oci = parse_args(args=args).operation
         build_oci.backend.add_dir('/build/test-directory')
         build_oci.build()
-        self.assertThat(build_oci.backend.run.calls[0], RanBuildCommand(
-            ["docker", "build", "--no-cache",
-             "--build-arg", "http_proxy=http://proxy.example:3128/";,
-             "--build-arg", "https_proxy=http://proxy.example:3128/";,
-             "--tag", "test-image", "/home/buildd/test-image/."],
-            cwd="/home/buildd/test-image"))
-        self.assertRanPostBuildCommands(build_oci)
+        self.assertThat(build_oci.backend.run.calls, MatchesListwise([
+            RanBuildCommand(
+                ["docker", "build", "--no-cache",
+                 "--build-arg", "http_proxy=http://proxy.example:3128/";,
+                 "--build-arg", "https_proxy=http://proxy.example:3128/";,
+                 "--tag", "test-image", "/home/buildd/test-image/."],
+                cwd="/home/buildd/test-image"),
+            ]))
 
     def test_run_succeeds(self):
         args = [