| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| # Pylint doesn't play well with fixtures and dependency injection from pytest |
| # pylint: disable=redefined-outer-name |
| |
| import os |
| |
| import pytest |
| |
| from buildstream import _yaml |
| from buildstream._project import Project |
| from buildstream._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 |
| from buildstream._testing import cli # pylint: disable=unused-import |
| |
| from tests.testutils import create_artifact_share, dummy_context |
| |
| |
| # Project directory |
| DATA_DIR = os.path.join( |
| os.path.dirname(os.path.realpath(__file__)), |
| "project", |
| ) |
| |
| |
| def tree_maker(cas, tree, directory): |
| if tree.root.ByteSize() == 0: |
| tree.root.CopyFrom(directory) |
| |
| for directory_node in directory.directories: |
| child_directory = tree.children.add() |
| |
| with open(cas.objpath(directory_node.digest), "rb") as f: |
| child_directory.ParseFromString(f.read()) |
| |
| tree_maker(cas, tree, child_directory) |
| |
| |
| @pytest.mark.datafiles(DATA_DIR) |
| def test_pull(cli, tmpdir, datafiles): |
| project_dir = str(datafiles) |
| |
| # Set up an artifact cache. |
| with create_artifact_share(os.path.join(str(tmpdir), "artifactshare")) as share: |
| # Configure artifact share |
| cache_dir = os.path.join(str(tmpdir), "cache") |
| user_config_file = str(tmpdir.join("buildstream.conf")) |
| user_config = { |
| "scheduler": {"pushers": 1}, |
| "artifacts": { |
| "servers": [ |
| { |
| "url": share.repo, |
| "push": True, |
| } |
| ] |
| }, |
| "cachedir": cache_dir, |
| } |
| |
| # Write down the user configuration file |
| _yaml.roundtrip_dump(user_config, file=user_config_file) |
| # Ensure CLI calls will use it |
| cli.configure(user_config) |
| |
| # First build the project with the artifact cache configured |
| result = cli.run(project=project_dir, args=["build", "target.bst"]) |
| result.assert_success() |
| |
| # Assert that we are now cached locally |
| assert cli.get_element_state(project_dir, "target.bst") == "cached" |
| # Assert that we shared/pushed the cached artifact |
| assert share.get_artifact(cli.get_artifact_name(project_dir, "test", "target.bst")) |
| |
| # Delete the artifact locally |
| cli.remove_artifact_from_cache(project_dir, "target.bst") |
| |
| # Assert that we are not cached locally anymore |
| assert cli.get_element_state(project_dir, "target.bst") != "cached" |
| |
| with dummy_context(config=user_config_file) as context: |
| # Load the project |
| project = Project(project_dir, context) |
| project.ensure_fully_loaded() |
| |
| # Assert that the element's artifact is **not** cached |
| element = project.load_elements(["target.bst"])[0] |
| element_key = cli.get_element_key(project_dir, "target.bst") |
| assert not cli.artifact.is_cached(cache_dir, element, element_key) |
| |
| context.cachedir = cache_dir |
| context.casdir = os.path.join(cache_dir, "cas") |
| context.tmpdir = os.path.join(cache_dir, "tmp") |
| |
| # Load the project manually |
| project = Project(project_dir, context) |
| project.ensure_fully_loaded() |
| |
| # Create a local artifact cache handle |
| artifactcache = context.artifactcache |
| |
| # Initialize remotes |
| context.initialize_remotes(True, True, None, None) |
| |
| assert artifactcache.has_push_remotes(plugin=element), "No remote configured for element target.bst" |
| assert artifactcache.pull(element, element_key), "Pull operation failed" |
| |
| assert cli.artifact.is_cached(cache_dir, element, element_key) |
| |
| |
| @pytest.mark.datafiles(DATA_DIR) |
| def test_pull_tree(cli, tmpdir, datafiles): |
| project_dir = str(datafiles) |
| |
| # Set up an artifact cache. |
| with create_artifact_share(os.path.join(str(tmpdir), "artifactshare")) as share: |
| # Configure artifact share |
| rootcache_dir = os.path.join(str(tmpdir), "cache") |
| user_config_file = str(tmpdir.join("buildstream.conf")) |
| user_config = { |
| "scheduler": {"pushers": 1}, |
| "artifacts": { |
| "servers": [ |
| { |
| "url": share.repo, |
| "push": True, |
| } |
| ] |
| }, |
| "cachedir": rootcache_dir, |
| } |
| |
| # Write down the user configuration file |
| _yaml.roundtrip_dump(user_config, file=user_config_file) |
| # Ensure CLI calls will use it |
| cli.configure(user_config) |
| |
| # First build the project with the artifact cache configured |
| result = cli.run(project=project_dir, args=["build", "target.bst"]) |
| result.assert_success() |
| |
| # Assert that we are now cached locally |
| assert cli.get_element_state(project_dir, "target.bst") == "cached" |
| # Assert that we shared/pushed the cached artifact |
| assert share.get_artifact(cli.get_artifact_name(project_dir, "test", "target.bst")) |
| |
| with dummy_context(config=user_config_file) as context: |
| # Load the project and CAS cache |
| project = Project(project_dir, context) |
| project.ensure_fully_loaded() |
| cas = context.get_cascache() |
| |
| # Assert that the element's artifact is cached |
| element = project.load_elements(["target.bst"])[0] |
| element_key = cli.get_element_key(project_dir, "target.bst") |
| assert cli.artifact.is_cached(rootcache_dir, element, element_key) |
| |
| # Retrieve the Directory object from the cached artifact |
| artifact_digest = cli.artifact.get_digest(rootcache_dir, element, element_key) |
| |
| # Initialize remotes |
| context.initialize_remotes(True, True, None, None) |
| |
| artifactcache = context.artifactcache |
| assert artifactcache.has_push_remotes() |
| |
| directory = remote_execution_pb2.Directory() |
| |
| with open(cas.objpath(artifact_digest), "rb") as f: |
| directory.ParseFromString(f.read()) |
| |
| # Build the Tree object while we are still cached |
| tree = remote_execution_pb2.Tree() |
| tree_maker(cas, tree, directory) |
| |
| # Push the Tree as a regular message |
| _, remotes = artifactcache.get_remotes(project.name, True) |
| assert len(remotes) == 1 |
| tree_digest = remotes[0].push_message(tree) |
| tree_hash, tree_size = tree_digest.hash, tree_digest.size_bytes |
| assert tree_hash and tree_size |
| |
| # Now delete the artifact locally |
| cli.remove_artifact_from_cache(project_dir, "target.bst") |
| |
| # Assert that we are not cached locally anymore |
| artifactcache.release_resources() |
| cas._casd_channel.request_shutdown() |
| cas.close_grpc_channels() |
| assert cli.get_element_state(project_dir, "target.bst") != "cached" |
| |
| tree_digest = remote_execution_pb2.Digest(hash=tree_hash, size_bytes=tree_size) |
| |
| # Pull the artifact using the Tree object |
| _, remotes = artifactcache.get_remotes(project.name, False) |
| assert len(remotes) == 1 |
| directory_digest = cas.pull_tree(remotes[0], tree_digest) |
| directory_hash, directory_size = directory_digest.hash, directory_digest.size_bytes |
| |
| # Directory size now zero with AaaP and stack element commit #1cbc5e63dc |
| assert directory_hash and not directory_size |
| |
| directory_digest = remote_execution_pb2.Digest(hash=directory_hash, size_bytes=directory_size) |
| |
| # Ensure the entire Tree stucture has been pulled |
| assert os.path.exists(cas.objpath(directory_digest)) |