Exemple #1
0
    def test_image_update_broken_kernel(
        self,
        bitbake_variables,
        connection,
        latest_mender_image,
        http_server,
        board_type,
        use_s3,
        s3_address,
    ):
        """Test that an update with a broken kernel rolls back correctly. This is
        distinct from the test_broken_image_update test, which corrupts the
        filesystem. When grub.d integration is enabled, these two scenarios
        trigger very different code paths."""

        file_flag = Helpers.get_file_flag(bitbake_variables)
        (active_before, passive_before) = determine_active_passive_part(
            bitbake_variables, connection)

        image_type = bitbake_variables["MENDER_DEVICE_TYPE"]

        temp_artifact = "temporary_artifact.mender"
        try:
            shutil.copyfile(latest_mender_image, temp_artifact)
            # Assume that artifact has the same kernel names as the currently
            # running image.
            kernels = connection.run(
                "find /boot/ -maxdepth 1 -name '*linu[xz]*' -o -name '*Image'"
            ).stdout.split()
            for kernel in kernels:
                # Inefficient, but there shouldn't be too many kernels.
                subprocess.check_call(
                    ["mender-artifact", "rm", f"{temp_artifact}:{kernel}"])

            Helpers.install_update(
                temp_artifact,
                connection,
                http_server,
                board_type,
                use_s3,
                s3_address,
            )

            reboot(connection)

            # Now qemu is auto-rebooted twice; once to boot the dummy image,
            # where it fails, and the boot loader auto-reboots a second time
            # into the original partition.

            output = run_after_connect("mount", connection)

            # The update should have reverted to the original active partition,
            # since the kernel was missing.
            assert output.find(active_before) >= 0
            assert output.find(passive_before) < 0

        finally:
            os.remove(temp_artifact)
Exemple #2
0
    def test_broken_image_update(self, bitbake_variables, connection):
        """Test that an update with a broken filesystem rolls back correctly."""

        file_flag = Helpers.get_file_flag(bitbake_variables)
        (active_before, passive_before) = determine_active_passive_part(
            bitbake_variables, connection)

        image_type = bitbake_variables["MENDER_DEVICE_TYPE"]

        try:
            # Make a dummy/broken update
            retcode = subprocess.call(
                "dd if=/dev/zero of=image.dat bs=1M count=0 seek=16",
                shell=True)
            if retcode != 0:
                raise Exception("error creating dummy image")
            retcode = subprocess.call(
                "mender-artifact write rootfs-image -t %s -n test-update %s image.dat -o image.mender"
                % (image_type, file_flag),
                shell=True,
            )
            if retcode != 0:
                raise Exception(
                    "error writing mender artifact using command: mender-artifact write rootfs-image -t %s -n test-update %s image.dat -o image.mender"
                    % (image_type, file_flag))

            put_no_sftp("image.mender",
                        connection,
                        remote="/var/tmp/image.mender")
            connection.run("mender install /var/tmp/image.mender")
            reboot(connection)

            # Now qemu is auto-rebooted twice; once to boot the dummy image,
            # where it fails, and the boot loader auto-reboots a second time
            # into the original partition.

            output = run_after_connect("mount", connection)

            # The update should have reverted to the original active partition,
            # since the image was bogus.
            assert output.find(active_before) >= 0
            assert output.find(passive_before) < 0

        finally:
            # Cleanup.
            if os.path.exists("image.mender"):
                os.remove("image.mender")
            if os.path.exists("image.dat"):
                os.remove("image.dat")
Exemple #3
0
    def do_install_mender_binary_delta(
        self,
        request,
        prepared_test_build,
        bitbake_variables,
        bitbake_image,
        connection,
        http_server,
        board_type,
        use_s3,
        s3_address,
    ):
        build_image(
            prepared_test_build["build_dir"],
            prepared_test_build["bitbake_corebase"],
            bitbake_image,
            ['IMAGE_INSTALL_append = " mender-binary-delta"'],
            [
                'BBLAYERS_append = " %s/../meta-mender-commercial"' %
                bitbake_variables["LAYERDIR_MENDER"]
            ],
        )

        image = latest_build_artifact(request,
                                      prepared_test_build["build_dir"],
                                      "core-image*.mender")

        Helpers.install_update(image, connection, http_server, board_type,
                               use_s3, s3_address)

        reboot(connection)

        run_after_connect("true", connection)
        connection.run("mender -commit")

        return image
Exemple #4
0
    def test_perform_update(
        self,
        request,
        setup_board,
        prepared_test_build,
        bitbake_variables,
        bitbake_image,
        connection,
        http_server,
        board_type,
        use_s3,
        s3_address,
    ):
        """Perform a delta update.

        """

        if ("read-only-rootfs"
                not in bitbake_variables["IMAGE_FEATURES"].strip().split()):
            pytest.skip("Only works when using read-only-rootfs IMAGE_FEATURE")

        if distutils.spawn.find_executable(
                "mender-binary-delta-generator") is None:
            pytest.fail("mender-binary-delta-generator not found in PATH")

        built_artifact = self.do_install_mender_binary_delta(
            request,
            prepared_test_build,
            bitbake_variables,
            bitbake_image,
            connection,
            http_server,
            board_type,
            use_s3,
            s3_address,
        )

        with make_tempdir() as tmpdir:
            # Copy previous build
            artifact_from = os.path.join(tmpdir, "artifact_from.mender")
            shutil.copyfile(built_artifact, artifact_from)

            # Create new image installing some extra software
            build_image(
                prepared_test_build["build_dir"],
                prepared_test_build["bitbake_corebase"],
                bitbake_image,
                ['IMAGE_INSTALL_append = " nano"'],
            )
            built_artifact = latest_build_artifact(
                request, prepared_test_build["build_dir"],
                "core-image*.mender")
            artifact_to = os.path.join(tmpdir, "artifact_to.mender")
            shutil.copyfile(built_artifact, artifact_to)

            # Create delta Artifact using mender-binary-delta-generator
            artifact_delta = os.path.join(tmpdir, "artifact_delta.mender")
            subprocess.check_call(
                f"mender-binary-delta-generator -n v2.0-deltafrom-v1.0 {artifact_from} {artifact_to} -o {artifact_delta}",
                shell=True,
            )

            # Verbose provides/depends of the different Artifacts and the client (when supported)
            connection.run("mender show-provides", warn=True)
            subprocess.check_call(
                "mender-artifact read %s" % artifact_from,
                shell=True,
            )
            subprocess.check_call(
                "mender-artifact read %s" % artifact_to,
                shell=True,
            )
            subprocess.check_call(
                "mender-artifact read %s" % artifact_delta,
                shell=True,
            )

            # Install Artifact, verify partitions and commit
            (active,
             passive) = determine_active_passive_part(bitbake_variables,
                                                      connection)
            Helpers.install_update(artifact_delta, connection, http_server,
                                   board_type, use_s3, s3_address)
            reboot(connection)
            run_after_connect("true", connection)
            (new_active, new_passive) = determine_active_passive_part(
                bitbake_variables, connection)
            assert new_active == passive
            assert new_passive == active
            connection.run("mender -commit")
Exemple #5
0
    def test_redundant_grub_env(self, successful_image_update_mender,
                                bitbake_variables, connection):
        """This tests pretty much the same thing as the test_redundant_uboot_env
        above, but the details differ. U-Boot maintains a counter in each
        environment, and then only updates one of them. However, the GRUB
        variant we have implemented in the GRUB scripting language, where we
        cannot do this, so instead we update both, and use the validity of the
        variables instead as a crude checksum."""

        (active,
         passive) = determine_active_passive_part(bitbake_variables,
                                                  connection)

        # Corrupt the passive partition.
        connection.run("dd if=/dev/zero of=%s bs=1024 count=1024" % passive)

        if ("mender-bios" in bitbake_variables.get("MENDER_FEATURES",
                                                   "").split()
                or "mender-bios" in bitbake_variables.get(
                    "DISTRO_FEATURES", "").split()):
            env_dir = "/boot/grub/grub-mender-grubenv"
        else:
            env_dir = "/boot/efi/grub-mender-grubenv"

        # Now try to corrupt the environment, and make sure it doesn't get booted into.
        for env_num in [1, 2]:
            # Make a copy of the two environments.
            connection.run(
                "cp %s/{mender_grubenv1/env,mender_grubenv1/env.backup}" %
                env_dir)
            connection.run(
                "cp %s/{mender_grubenv1/lock,mender_grubenv1/lock.backup}" %
                env_dir)
            connection.run(
                "cp %s/{mender_grubenv2/env,mender_grubenv2/env.backup}" %
                env_dir)
            connection.run(
                "cp %s/{mender_grubenv2/lock,mender_grubenv2/lock.backup}" %
                env_dir)

            try:
                env_file = "%s/mender_grubenv%d/env" % (env_dir, env_num)
                lock_file = "%s/mender_grubenv%d/lock" % (env_dir, env_num)
                connection.run('sed -e "s/editing=.*/editing=1/" %s' %
                               lock_file)
                connection.run(
                    'sed -e "s/mender_boot_part=.*/mender_boot_part=%s/" %s' %
                    (passive[-1], lock_file))

                reboot(connection)
                run_after_connect("true", connection)

                (new_active, new_passive) = determine_active_passive_part(
                    bitbake_variables, connection)
                assert new_active == active
                assert new_passive == passive

            finally:
                # Restore the two environments.
                connection.run(
                    "mv %s/{mender_grubenv1/env.backup,mender_grubenv1/env}" %
                    env_dir)
                connection.run(
                    "mv %s/{mender_grubenv1/lock.backup,mender_grubenv1/lock}"
                    % env_dir)
                connection.run(
                    "mv %s/{mender_grubenv2/env.backup,mender_grubenv2/env}" %
                    env_dir)
                connection.run(
                    "mv %s/{mender_grubenv2/lock.backup,mender_grubenv2/lock}"
                    % env_dir)
Exemple #6
0
    def test_network_based_image_update(
        self,
        successful_image_update_mender,
        bitbake_variables,
        connection,
        http_server,
        board_type,
        use_s3,
        s3_address,
    ):

        (active_before, passive_before) = determine_active_passive_part(
            bitbake_variables, connection)

        Helpers.install_update(
            successful_image_update_mender,
            connection,
            http_server,
            board_type,
            use_s3,
            s3_address,
        )

        bootenv_print, _ = bootenv_tools(connection)

        output = connection.run(f"{bootenv_print} bootcount").stdout
        assert output.rstrip("\n") == "bootcount=0"

        output = connection.run(f"{bootenv_print} upgrade_available").stdout
        assert output.rstrip("\n") == "upgrade_available=1"

        output = connection.run(f"{bootenv_print} mender_boot_part").stdout
        assert output.rstrip("\n") == "mender_boot_part=" + passive_before[-1:]

        # Delete kernel and associated files from currently running partition,
        # so that the boot will fail if U-Boot for any reason tries to grab the
        # kernel from the wrong place.
        connection.run("rm -f /boot/* || true")

        reboot(connection)

        run_after_connect("true", connection)
        (active_after, passive_after) = determine_active_passive_part(
            bitbake_variables, connection)

        # The OS should have moved to a new partition, since the image was fine.
        assert active_after == passive_before
        assert passive_after == active_before

        output = connection.run(f"{bootenv_print} bootcount").stdout
        assert output.rstrip("\n") == "bootcount=1"

        output = connection.run(f"{bootenv_print} upgrade_available").stdout
        assert output.rstrip("\n") == "upgrade_available=1"

        output = connection.run(f"{bootenv_print} mender_boot_part").stdout
        assert output.rstrip("\n") == "mender_boot_part=" + active_after[-1:]

        connection.run("mender commit")

        output = connection.run(f"{bootenv_print} upgrade_available").stdout
        assert output.rstrip("\n") == "upgrade_available=0"

        output = connection.run(f"{bootenv_print} mender_boot_part").stdout
        assert output.rstrip("\n") == "mender_boot_part=" + active_after[-1:]

        active_before = active_after
        passive_before = passive_after

        reboot(connection)

        run_after_connect("true", connection)
        (active_after, passive_after) = determine_active_passive_part(
            bitbake_variables, connection)

        # The OS should have stayed on the same partition, since we committed.
        assert active_after == active_before
        assert passive_after == passive_before