Beispiel #1
0
def test_build_shell_fetch(cli, datafiles):
    project = str(datafiles)
    element_name = "build-shell-fetch.bst"

    # Create a file with unique contents such that it cannot be in the cache already
    test_filepath = os.path.join(project, "files", "hello.txt")
    test_message = "Hello World! {}".format(uuid.uuid4())
    with open(test_filepath, "w") as f:
        f.write(test_message)
    checksum = utils.sha256sum(test_filepath)

    # Create an element that has this unique file as a source
    element = {
        "kind": "manual",
        "depends": ["base.bst"],
        "sources": [{"kind": "remote", "url": "project_dir:/files/hello.txt", "ref": checksum}],
    }
    _yaml.roundtrip_dump(element, os.path.join(project, "elements", element_name))

    # Ensure our dependencies are cached
    result = cli.run(project=project, args=["build", "base.bst"])
    result.assert_success()

    # Ensure our sources are not cached
    assert cli.get_element_state(project, element_name) == "fetch needed"

    # Launching a shell should fetch any uncached sources
    result = cli.run(project=project, args=["shell", "--build", element_name, "cat", "hello.txt"])
    result.assert_success()
    assert result.output == test_message
Beispiel #2
0
def test_integration_partial_artifact(cli, datafiles, tmpdir,
                                      integration_cache):

    project = str(datafiles)
    element_name = "autotools/amhello.bst"

    # push to an artifact server so we can pull from it later.
    with create_artifact_share(os.path.join(str(tmpdir),
                                            "artifactshare")) as share:
        cli.configure(
            {"artifacts": {
                "servers": [{
                    "url": share.repo,
                    "push": True
                }]
            }})
        result = cli.run(project=project, args=["build", element_name])
        result.assert_success()

        # If the build is cached then it might not push to the artifact cache
        result = cli.run(project=project,
                         args=["artifact", "push", element_name])
        result.assert_success()

        result = cli.run(project=project, args=["shell", element_name])
        result.assert_success()

        # do a checkout and get the digest of the hello binary.
        result = cli.run(
            project=project,
            args=[
                "artifact",
                "checkout",
                "--deps",
                "none",
                "--directory",
                os.path.join(str(tmpdir), "tmp"),
                "autotools/amhello.bst",
            ],
        )
        result.assert_success()
        digest = utils.sha256sum(
            os.path.join(str(tmpdir), "tmp", "usr", "bin", "hello"))

        # Remove the binary from the CAS
        cachedir = cli.config["cachedir"]
        objpath = os.path.join(cachedir, "cas", "objects", digest[:2],
                               digest[2:])
        os.unlink(objpath)

        # check shell doesn't work
        result = cli.run(project=project,
                         args=["shell", element_name, "--", "hello"])
        result.assert_main_error(ErrorDomain.APP, "shell-missing-deps")

        # check the artifact gets completed with '--pull' specified
        result = cli.run(project=project,
                         args=["shell", "--pull", element_name, "--", "hello"])
        result.assert_success()
        assert "autotools/amhello.bst" in result.get_pulled_elements()
def test_partial_artifact_checkout_fetch(cli, datafiles, tmpdir):
    project = str(datafiles)
    checkout_dir = os.path.join(str(tmpdir), "checkout")

    repo = create_repo("git", str(tmpdir))
    repo.create(os.path.join(str(datafiles), "files"))
    element_dir = os.path.join(str(tmpdir), "elements")
    project = str(tmpdir)
    project_config = {
        "name": "partial-artifact-checkout-fetch",
        "min-version": "2.0",
        "element-path": "elements",
    }
    project_file = os.path.join(str(tmpdir), "project.conf")
    _yaml.roundtrip_dump(project_config, project_file)
    input_config = {
        "kind": "import",
        "sources": [repo.source_config()],
    }
    input_name = "input.bst"
    input_file = os.path.join(element_dir, input_name)
    _yaml.roundtrip_dump(input_config, input_file)

    with create_artifact_share(os.path.join(str(tmpdir),
                                            "artifactshare")) as share:

        cli.configure({"artifacts": {"url": share.repo, "push": True}})

        result = cli.run(project=project, args=["source", "track", input_name])
        result.assert_success()
        result = cli.run(project=project, args=["build", input_name])
        result.assert_success()

        # A push artifact cache means we have to pull to push to them, so
        # delete some blobs from that CAS such that we have to fetch
        digest = utils.sha256sum(
            os.path.join(project, "files", "bin-files", "usr", "bin", "hello"))
        objpath = os.path.join(cli.directory, "cas", "objects", digest[:2],
                               digest[2:])
        os.unlink(objpath)

        # Verify that the build-only dependency is not (complete) in the local cache
        result = cli.run(project=project,
                         args=[
                             "artifact", "checkout", input_name, "--directory",
                             checkout_dir
                         ])
        result.assert_main_error(ErrorDomain.STREAM,
                                 "uncached-checkout-attempt")

        # Verify that the pull method fetches relevant artifacts in order to stage
        result = cli.run(project=project,
                         args=[
                             "artifact", "checkout", "--pull", input_name,
                             "--directory", checkout_dir
                         ])
        result.assert_success()

        # should have pulled whatever was deleted previous
        assert input_name in result.get_pulled_elements()
Beispiel #4
0
def test_pull_missing_local_blob(cli, tmpdir, datafiles):
    project = os.path.join(datafiles.dirname, datafiles.basename)
    repo = create_repo("git", str(tmpdir))
    repo.create(os.path.join(str(datafiles), "files"))
    element_dir = os.path.join(str(tmpdir), "elements")
    project = str(tmpdir)
    project_config = {
        "name": "pull-missing-local-blob",
        "min-version": "2.0",
        "element-path": "elements",
    }
    project_file = os.path.join(str(tmpdir), "project.conf")
    _yaml.roundtrip_dump(project_config, project_file)
    input_config = {
        "kind": "import",
        "sources": [repo.source_config()],
    }
    input_name = "input.bst"
    input_file = os.path.join(element_dir, input_name)
    _yaml.roundtrip_dump(input_config, input_file)

    depends_name = "depends.bst"
    depends_config = {"kind": "stack", "depends": [input_name]}
    depends_file = os.path.join(element_dir, depends_name)
    _yaml.roundtrip_dump(depends_config, depends_file)

    with create_artifact_share(os.path.join(str(tmpdir),
                                            "artifactshare")) as share:

        # First build the import-bin element and push to the remote.
        cli.configure(
            {"artifacts": {
                "servers": [{
                    "url": share.repo,
                    "push": True
                }]
            }})

        result = cli.run(project=project, args=["source", "track", input_name])
        result.assert_success()
        result = cli.run(project=project, args=["build", input_name])
        result.assert_success()
        assert cli.get_element_state(project, input_name) == "cached"

        # Delete a file blob from the local cache.
        # This is a placeholder to test partial CAS handling until we support
        # partial artifact pulling (or blob-based CAS expiry).
        #
        digest = utils.sha256sum(
            os.path.join(project, "files", "bin-files", "usr", "bin", "hello"))
        objpath = os.path.join(cli.directory, "cas", "objects", digest[:2],
                               digest[2:])
        os.unlink(objpath)

        # Now try bst build
        result = cli.run(project=project, args=["build", depends_name])
        result.assert_success()

        # Assert that the import-bin artifact was pulled (completing the partial artifact)
        assert result.get_pulled_elements() == [input_name]
Beispiel #5
0
    def _download(self, url):

        try:
            with self.cargo.tempdir() as td:
                default_name = os.path.basename(url)
                request = urllib.request.Request(url)
                request.add_header('Accept', '*/*')

                # We do not use etag in case what we have in cache is
                # not matching ref in order to be able to recover from
                # corrupted download.
                if self.sha:
                    etag = self._get_etag(self.sha)
                    if etag and self.get_consistency() == Consistency.CACHED:
                        request.add_header('If-None-Match', etag)

                with contextlib.closing(
                        urllib.request.urlopen(request)) as response:
                    info = response.info()

                    etag = info['ETag'] if 'ETag' in info else None

                    filename = info.get_filename(default_name)
                    filename = os.path.basename(filename)
                    local_file = os.path.join(td, filename)
                    with open(local_file, 'wb') as dest:
                        shutil.copyfileobj(response, dest)

                # Make sure url-specific mirror dir exists.
                os.makedirs(self._get_mirror_dir(), exist_ok=True)

                # Store by sha256sum
                sha256 = utils.sha256sum(local_file)
                # Even if the file already exists, move the new file over.
                # In case the old file was corrupted somehow.
                os.rename(local_file, self._get_mirror_file(sha256))

                if etag:
                    self._store_etag(sha256, etag)
                return sha256

        except urllib.error.HTTPError as e:
            if e.code == 304:
                # 304 Not Modified.
                # Because we use etag only for matching sha, currently specified sha is what
                # we would have downloaded.
                return self.sha
            raise SourceError("{}: Error mirroring {}: {}".format(
                self, url, e),
                              temporary=True) from e

        except (urllib.error.URLError, urllib.error.ContentTooShortError,
                OSError) as e:
            raise SourceError("{}: Error mirroring {}: {}".format(
                self, url, e),
                              temporary=True) from e
    def create(self, directory):
        tarball = os.path.join(self.repo, "file.tar.gz")

        old_dir = os.getcwd()
        os.chdir(directory)
        with tarfile.open(tarball, "w:gz") as tar:
            tar.add(".")
        os.chdir(old_dir)

        return sha256sum(tarball)
Beispiel #7
0
def generate_remote_import_element(input_path, output_path):
    return {
        "kind":
        "import",
        "sources": [{
            "kind": "remote",
            "url": "file://{}".format(input_path),
            "filename": output_path,
            "ref": utils.sha256sum(input_path),
        }],
    }
Beispiel #8
0
    def create(self, directory):
        archive = os.path.join(self.repo, "file.zip")

        old_dir = os.getcwd()
        os.chdir(directory)
        with zipfile.ZipFile(archive, "w") as zipfp:
            for root, dirs, files in os.walk("."):
                names = dirs + files
                names = [os.path.join(root, name) for name in names]

                for name in names:
                    zipfp.write(name)

        os.chdir(old_dir)

        return sha256sum(archive)
Beispiel #9
0
 def get_unique_key(self):
     return [self.path, utils.sha256sum(self.fullpath), self.strip_level]
Beispiel #10
0
    def _ensure_mirror(self):
        # Downloads from the url and caches it according to its sha256sum.
        try:
            with self.tempdir() as td:
                default_name = os.path.basename(self.url)
                request = urllib.request.Request(self.url)
                request.add_header("Accept", "*/*")

                # We do not use etag in case what we have in cache is
                # not matching ref in order to be able to recover from
                # corrupted download.
                if self.ref:
                    etag = self._get_etag(self.ref)

                    # Do not re-download the file if the ETag matches.
                    if etag and self.is_cached():
                        request.add_header("If-None-Match", etag)

                opener = self.__get_urlopener()
                with contextlib.closing(opener.open(request)) as response:
                    info = response.info()

                    etag = info["ETag"] if "ETag" in info else None

                    filename = info.get_filename(default_name)
                    filename = os.path.basename(filename)
                    local_file = os.path.join(td, filename)
                    with open(local_file, "wb") as dest:
                        shutil.copyfileobj(response, dest)

                # Make sure url-specific mirror dir exists.
                if not os.path.isdir(self._mirror_dir):
                    os.makedirs(self._mirror_dir)

                # Store by sha256sum
                sha256 = utils.sha256sum(local_file)
                # Even if the file already exists, move the new file over.
                # In case the old file was corrupted somehow.
                os.rename(local_file, self._get_mirror_file(sha256))

                if etag:
                    self._store_etag(sha256, etag)
                return sha256

        except urllib.error.HTTPError as e:
            if e.code == 304:
                # 304 Not Modified.
                # Because we use etag only for matching ref, currently specified ref is what
                # we would have downloaded.
                return self.ref
            raise SourceError(
                "{}: Error mirroring {}: {}".format(self, self.url, e),
                temporary=True,
            ) from e

        except (
                urllib.error.URLError,
                urllib.error.ContentTooShortError,
                OSError,
                ValueError,
        ) as e:
            # Note that urllib.request.Request in the try block may throw a
            # ValueError for unknown url types, so we handle it here.
            raise SourceError(
                "{}: Error mirroring {}: {}".format(self, self.url, e),
                temporary=True,
            ) from e
 def _verify_blob(path, expected_digest):
     blob_digest = "sha256:" + sha256sum(path)
     if expected_digest != blob_digest:
         raise SourceError(
             "Blob {} is corrupt; got content hash of {}.".format(
                 path, blob_digest))