This is an automated email from the ASF dual-hosted git repository.

tvb pushed a commit to branch tristan/change-remote-config
in repository https://gitbox.apache.org/repos/asf/buildstream.git


The following commit(s) were added to refs/heads/tristan/change-remote-config 
by this push:
     new 82eac25  WIP: Change it all
82eac25 is described below

commit 82eac251f4c64c036d3606038551731932d2c766
Author: Tristan van Berkom <[email protected]>
AuthorDate: Tue Jan 26 22:22:15 2021 +0900

    WIP: Change it all
---
 src/buildstream/_artifactcache.py       |  49 ++--
 src/buildstream/_assetcache.py          | 385 ++++++++++----------------------
 src/buildstream/_context.py             | 169 +++++++++++---
 src/buildstream/_elementsourcescache.py |  13 +-
 src/buildstream/_sourcecache.py         |  15 +-
 src/buildstream/_stream.py              | 115 +++-------
 src/buildstream/element.py              |   1 +
 tests/artifactcache/capabilities.py     |   4 +-
 tests/artifactcache/config.py           |  16 +-
 tests/artifactcache/pull.py             |  11 +-
 tests/artifactcache/push.py             |  10 +-
 tests/frontend/pull.py                  |  16 +-
 tests/sourcecache/capabilities.py       |   6 +-
 13 files changed, 339 insertions(+), 471 deletions(-)

diff --git a/src/buildstream/_artifactcache.py 
b/src/buildstream/_artifactcache.py
index d73c7f0..a6bdc99 100644
--- a/src/buildstream/_artifactcache.py
+++ b/src/buildstream/_artifactcache.py
@@ -37,10 +37,6 @@ REMOTE_ASSET_ARTIFACT_URN_TEMPLATE = 
"urn:fdc:buildstream.build:2020:artifact:{}
 #     context (Context): The BuildStream context
 #
 class ArtifactCache(AssetCache):
-
-    spec_name = "artifact_cache_specs"
-    config_node_name = "artifacts"
-
     def __init__(self, context):
         super().__init__(context)
 
@@ -48,12 +44,6 @@ class ArtifactCache(AssetCache):
         self._basedir = context.artifactdir
         os.makedirs(self._basedir, exist_ok=True)
 
-    def update_mtime(self, ref):
-        try:
-            os.utime(os.path.join(self._basedir, ref))
-        except FileNotFoundError as e:
-            raise ArtifactError("Couldn't find artifact: {}".format(ref)) from 
e
-
     # preflight():
     #
     # Preflight check.
@@ -88,7 +78,7 @@ class ArtifactCache(AssetCache):
     #     ([str]) - A list of artifact names as generated in LRU order
     #
     def list_artifacts(self, *, glob=None):
-        return [ref for _, ref in 
sorted(list(self._list_refs_mtimes(self._basedir, glob_expr=glob)))]
+        return [ref for _, ref in 
sorted(list(self.list_refs_mtimes(self._basedir, glob_expr=glob)))]
 
     # remove():
     #
@@ -101,7 +91,7 @@ class ArtifactCache(AssetCache):
     #
     def remove(self, ref):
         try:
-            self._remove_ref(ref)
+            self.remove_ref(ref)
         except AssetCacheError as e:
             raise ArtifactError("{}".format(e)) from e
 
@@ -123,9 +113,7 @@ class ArtifactCache(AssetCache):
         project = element._get_project()
         display_key = element._get_display_key()
 
-        index_remotes = [r for r in self._index_remotes[project] if 
r.spec.push]
-        storage_remotes = [r for r in self._storage_remotes[project] if 
r.spec.push]
-
+        index_remotes, storage_remotes = self.get_remotes(project.name, True)
         artifact_proto = artifact._get_proto()
         artifact_digest = 
self.cas.add_object(buffer=artifact_proto.SerializeToString())
 
@@ -176,10 +164,12 @@ class ArtifactCache(AssetCache):
         artifact_name = element.get_artifact_name(key=key)
         uri = REMOTE_ASSET_ARTIFACT_URN_TEMPLATE.format(artifact_name)
 
+        index_remotes, storage_remotes = self.get_remotes(project.name, False)
+
         errors = []
         # Start by pulling our artifact proto, so that we know which
         # blobs to pull
-        for remote in self._index_remotes[project]:
+        for remote in index_remotes:
             remote.init()
             try:
                 element.status("Pulling artifact {} <- {}".format(display_key, 
remote))
@@ -205,7 +195,7 @@ class ArtifactCache(AssetCache):
 
         errors = []
         # If we do, we can pull it!
-        for remote in self._storage_remotes[project]:
+        for remote in storage_remotes:
             remote.init()
             try:
                 element.status("Pulling data for artifact {} <- 
{}".format(display_key, remote))
@@ -240,7 +230,8 @@ class ArtifactCache(AssetCache):
     #     digest (Digest): The digest of the tree
     #
     def pull_tree(self, project, digest):
-        for remote in self._storage_remotes[project]:
+        _, storage_remotes = self.get_remotes(project.name, False)
+        for remote in storage_remotes:
             digest = self.cas.pull_tree(remote, digest)
 
             if digest:
@@ -261,12 +252,7 @@ class ArtifactCache(AssetCache):
     #     (ArtifactError): if there was an error
     #
     def push_message(self, project, message):
-
-        if self._has_push_remotes:
-            push_remotes = [r for r in self._storage_remotes[project] if 
r.spec.push]
-        else:
-            push_remotes = []
-
+        _, push_remotes = self.get_remotes(project.name, True)
         if not push_remotes:
             raise ArtifactError(
                 "push_message was called, but no remote artifact " + "servers 
are configured as push remotes."
@@ -305,7 +291,9 @@ class ArtifactCache(AssetCache):
     #     missing_blobs (list): The Digests of the blobs to fetch
     #
     def fetch_missing_blobs(self, project, missing_blobs):
-        for remote in self._index_remotes[project]:
+
+        index_remotes, _ = self.get_remotes(project.name, False)
+        for remote in index_remotes:
             if not missing_blobs:
                 break
 
@@ -332,8 +320,7 @@ class ArtifactCache(AssetCache):
         if not missing_blobs:
             return []
 
-        push_remotes = [r for r in self._storage_remotes[project] if 
r.spec.push]
-
+        _, push_remotes = self.get_remotes(project.name, True)
         remote_missing_blobs_list = []
 
         for remote in push_remotes:
@@ -358,13 +345,15 @@ class ArtifactCache(AssetCache):
     #    (bool): True if the element is available remotely
     #
     def check_remotes_for_element(self, element):
+        project = element._get_project()
+        index_remotes, _ = self.get_remotes(project.name, False)
+
         # If there are no remotes
-        if not self._index_remotes:
+        if not index_remotes:
             return False
 
-        project = element._get_project()
         ref = element.get_artifact_name()
-        for remote in self._index_remotes[project]:
+        for remote in index_remotes:
             remote.init()
 
             if self._query_remote(ref, remote):
diff --git a/src/buildstream/_assetcache.py b/src/buildstream/_assetcache.py
index eff9de9..66e2182 100644
--- a/src/buildstream/_assetcache.py
+++ b/src/buildstream/_assetcache.py
@@ -18,13 +18,12 @@
 #
 import os
 import re
-from itertools import chain
-from typing import TYPE_CHECKING
+from typing import List, Dict, Tuple, Iterable, Optional
 import grpc
 
 from . import utils
 from .node import MappingNode
-from ._cas import CASRemote
+from ._cas import CASRemote, CASCache
 from ._exceptions import AssetCacheError, RemoteError
 from ._remotespec import RemoteSpec, RemoteType
 from ._remote import BaseRemote
@@ -32,14 +31,9 @@ from ._protos.build.bazel.remote.asset.v1 import 
remote_asset_pb2, remote_asset_
 from ._protos.google.rpc import code_pb2
 
 
-if TYPE_CHECKING:
-    from typing import Optional, Type
-    from ._exceptions import BstError
-
-
 class AssetRemote(BaseRemote):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
+    def __init__(self, spec):
+        super().__init__(spec)
         self.fetch_service = None
         self.push_service = None
 
@@ -257,161 +251,137 @@ class AssetRemote(BaseRemote):
             raise AssetCacheError("PushDirectory failed with status {}: 
{}".format(e.code().name, e.details())) from e
 
 
-# Base Asset Cache for Caches to derive from
+# RemotePair()
 #
-class AssetCache:
+# A pair of remotes which corresponds to a RemoteSpec, we
+# need separate remote objects for the index and the storage so
+# we store them together for each RemoteSpec here.
+#
+# Either members of the RemotePair may be None, in case that
+# the user specified a diffrerent RemoteSpec for indexing and
+# for storage.
+#
+# Both members may also be None, in the case that we were unable
+# to establish a connection to this remote at initialization time.
+#
+class RemotePair:
+    def __init__(self, cas: CASCache, spec: RemoteSpec):
+        self.index: Optional[AssetRemote] = None
+        self.storage: Optional[CASRemote] = None
+        self.error: Optional[str] = None
 
-    # None of these should ever be called in the base class, but this appeases
-    # pylint to some degree
-    spec_name = None  # type: str
-    config_node_name = None  # type: str
+        try:
+            if spec.remote_type in [RemoteType.INDEX, RemoteType.ALL]:
+                index = AssetRemote(spec)
+                index.check()
+                self.index = index
+            if spec.remote_type in [RemoteType.STORAGE, RemoteType.ALL]:
+                storage = CASRemote(spec, cas)
+                storage.check()
+                self.storage = storage
+        except RemoteError as e:
+            self.error = str(e)
 
+
+# Base Asset Cache for Caches to derive from
+#
+class AssetCache:
     def __init__(self, context):
         self.context = context
-        self.cas = context.get_cascache()
+        self.cas: CASCache = context.get_cascache()
 
-        self._remotes_setup = False  # Check to prevent double-setup of remotes
-        # Per-project list of Remote instances.
-        self._storage_remotes = {}
-        self._index_remotes = {}
+        # Table of RemotePair objects
+        self._remotes: Dict[RemoteSpec, RemotePair] = {}
 
-        self.global_remote_specs = []
-        self.project_remote_specs = {}
+        # Table of prioritized RemoteSpecs which are valid for each project
+        self._project_specs: Dict[str, List[RemoteSpec]] = {}
 
-        self._has_fetch_remotes = False
-        self._has_push_remotes = False
+        self._has_fetch_remotes: bool = False
+        self._has_push_remotes: bool = False
 
         self._basedir = None
 
-    # close_grpc_channels():
-    #
-    # Close open gRPC channels.
-    #
-    def close_grpc_channels(self):
-        # Close all remotes and their gRPC channels
-        for project_remotes in chain(self._index_remotes.values(), 
self._storage_remotes.values()):
-            for remote in project_remotes:
-                remote.close()
-
     # release_resources():
     #
     # Release resources used by AssetCache.
     #
     def release_resources(self):
-        self.close_grpc_channels()
 
-    # specs_from_config_node()
+        # Close all remotes and their gRPC channels
+        for remote in self._remotes.values():
+            if remote.index:
+                remote.index.close()
+            if remote.storage:
+                remote.storage.close()
+
+    # setup_remotes():
     #
-    # Parses the configuration of remote artifact caches from a config block.
+    # Sets up which remotes to use
     #
     # Args:
-    #   config_node (dict): The config block, which may contain a key defined 
by cls.config_node_name
-    #   basedir (str): The base directory for relative paths
+    #    specs: The active remote specs
+    #    project_specs: List of specs for each project
     #
-    # Returns:
-    #   A list of RemoteSpec instances.
-    #
-    # Raises:
-    #   LoadError, if the config block contains invalid keys.
-    #
-    @classmethod
-    def specs_from_config_node(cls, config_node, basedir=None):
-        cache_specs = []
-        artifacts = config_node.get_sequence(cls.config_node_name, default=[], 
allowed_types=[MappingNode])
+    def setup_remotes(self, specs: Iterable[RemoteSpec], project_specs: 
Dict[str, List[RemoteSpec]]):
+
+        # Hold on to the project specs
+        self._project_specs = project_specs
 
-        for spec_node in artifacts:
-            cache_specs.append(RemoteSpec.new_from_node(spec_node))
+        with self.context.messenger.timed_activity("Initializing remote 
caches", silent_nested=True):
+            for spec in specs:
+                # This can be called multiple times, ensure that we only try
+                # to instantiate each remote once.
+                #
+                if spec in self._remotes:
+                    continue
 
-        return cache_specs
+                remote = RemotePair(self.cas, spec)
+                if remote.error:
+                    self.context.messenger.warn("Failed to initialize remote 
{}: {}".format(spec.url, remote.error))
 
-    # setup_remotes():
+                self._remotes[spec] = remote
+
+        # Determine overall existance of push or fetch remotes
+        self._has_fetch_remotes = any(remote.storage for _, remote in 
self._remotes.items()) and any(
+            remote.index for _, remote in self._remotes.items()
+        )
+        self._has_push_remotes = any(spec.push and remote.storage for spec, 
remote in self._remotes.items()) and any(
+            spec.push and remote.index for spec, remote in 
self._remotes.items()
+        )
+
+    # get_remotes():
     #
-    # Sets up which remotes to use
+    # List the index remotes and storage remotes available for fetching
     #
     # Args:
-    #    use_config (bool): Whether to use project configuration
-    #    remote_url (str): Remote cache URL
-    #    reinitialize (bool): Whether to re-initialize the remotes a second 
time
-    #
-    # This requires that all of the projects which are to be processed in the 
session
-    # have already been loaded and are observable in the Context.
-    #
-    def setup_remotes(self, *, use_config=False, remote_url=None, 
reinitialize=False):
-
-        # In some edge cases we need to reinitialize
-        if reinitialize:
-            self._storage_remotes = {}
-            self._index_remotes = {}
-            self._remotes_setup = False
-
-        # Ensure we do not double-initialise since this can be expensive
-        if self._remotes_setup:
-            return
-
-        self._remotes_setup = True
-
-        # Initialize remote caches. We allow the commandline to override
-        # the user config in some cases (for example `bst artifact push 
--remote=...`).
-        has_remote_caches = False
-        if remote_url:
-            self._set_remotes([RemoteSpec(RemoteType.ALL, remote_url, 
push=True)])
-            has_remote_caches = True
-        if use_config:
-            for project in self.context.get_projects():
-                caches = self._configured_remote_cache_specs(self.context, 
project)
-                if caches:  # caches is a list of RemoteSpec instances
-                    self._set_remotes(caches, project=project)
-                    has_remote_caches = True
-
-        def remote_failed(remote, error):
-            self.context.messenger.warn("Failed to initialize remote {}: 
{}".format(remote.url, error))
-
-        if has_remote_caches:
-            with self.context.messenger.timed_activity("Initializing remote 
caches", silent_nested=True):
-                self.initialize_remotes(on_failure=remote_failed)
-
-    # Notify remotes that forking is disabled
-    def notify_fork_disabled(self):
-        for project in self._index_remotes:
-            for remote in self._index_remotes[project]:
-                remote.notify_fork_disabled()
-        for project in self._storage_remotes:
-            for remote in self._storage_remotes[project]:
-                remote.notify_fork_disabled()
-
-    # initialize_remotes():
-    #
-    # This will contact each remote cache.
+    #    project_name: The project name
+    #    push: Whether pushing is required for this remote
     #
-    # Args:
-    #     on_failure (callable): Called if we fail to contact one of the 
caches.
+    # Returns:
+    #    index_remotes: The index remotes
+    #    storage_remotes: The storage remotes
     #
-    def initialize_remotes(self, *, on_failure=None):
-        index_remotes, storage_remotes = 
self._create_remote_instances(on_failure=on_failure)
-
-        # Assign remote instances to their respective projects
-        for project in self.context.get_projects():
-            # Get the list of specs that should be considered for this
-            # project
-            remote_specs = self.global_remote_specs.copy()
-            if project in self.project_remote_specs:
-                remote_specs.extend(self.project_remote_specs[project])
+    def get_remotes(self, project_name: str, push: bool) -> 
Tuple[List[AssetRemote], List[CASRemote]]:
+        try:
+            project_specs = self._project_specs[project_name]
+        except KeyError:
+            # Technically this shouldn't happen, but here is a defensive 
return none the less.
+            return [], []
 
-            # De-duplicate the list
-            remote_specs = list(utils._deduplicate(remote_specs))
+        index_remotes = []
+        storage_remotes = []
+        for spec in project_specs:
 
-            def get_remotes(remote_list, remote_specs):
-                for remote_spec in remote_specs:
-                    # If a remote_spec didn't make it into the remotes
-                    # dict, that means we can't access it, and it has been
-                    # disabled for this session.
-                    if remote_spec not in remote_list:
-                        continue
+            if push and not spec.push:
+                continue
 
-                    yield remote_list[remote_spec]
+            remote = self._remotes[spec]
+            if remote.index:
+                index_remotes.append(remote.index)
+            if remote.storage:
+                storage_remotes.append(remote.storage)
 
-            self._index_remotes[project] = list(get_remotes(index_remotes, 
remote_specs))
-            self._storage_remotes[project] = list(get_remotes(storage_remotes, 
remote_specs))
+        return index_remotes, storage_remotes
 
     # has_fetch_remotes():
     #
@@ -430,9 +400,10 @@ class AssetCache:
             # At least one (sub)project has fetch remotes
             return True
         else:
+            project = plugin._get_project()
+            index_remotes, storage_remotes = self.get_remotes(project.name, 
False)
+
             # Check whether the specified element's project has fetch remotes
-            index_remotes = self._index_remotes[plugin._get_project()]
-            storage_remotes = self._storage_remotes[plugin._get_project()]
             return index_remotes and storage_remotes
 
     # has_push_remotes():
@@ -452,143 +423,13 @@ class AssetCache:
             # At least one (sub)project has push remotes
             return True
         else:
-            # Check whether the specified element's project has push remotes
-            index_remotes = self._index_remotes[plugin._get_project()]
-            storage_remotes = self._storage_remotes[plugin._get_project()]
-            return any(remote.spec.push for remote in index_remotes) and any(
-                remote.spec.push for remote in storage_remotes
-            )
-
-    ################################################
-    #               Local Private Methods          #
-    ################################################
+            project = plugin._get_project()
+            index_remotes, storage_remotes = self.get_remotes(project.name, 
True)
 
-    # _configured_remote_cache_specs():
-    #
-    # Return the list of configured remotes for a given project, in priority
-    # order. This takes into account the user and project configuration.
-    #
-    # Args:
-    #     context (Context): The BuildStream context
-    #     project (Project): The BuildStream project
-    #
-    # Returns:
-    #   A list of RemoteSpec instances describing the remote caches.
-    #
-    @classmethod
-    def _configured_remote_cache_specs(cls, context, project):
-        project_overrides = context.get_overrides(project.name)
-        project_extra_specs = cls.specs_from_config_node(project_overrides)
-
-        project_specs = getattr(project, cls.spec_name)
-        context_specs = getattr(context, cls.spec_name)
-
-        return list(utils._deduplicate(project_extra_specs + project_specs + 
context_specs))
-
-    # _create_remote_instances():
-    #
-    # Create the global set of Remote instances, including
-    # project-specific and global instances, ensuring that all of them
-    # are accessible.
-    #
-    # Args:
-    #     on_failure (Callable[[Remote,Exception],None]):
-    #     What do do when a remote doesn't respond.
-    #
-    # Returns:
-    #    (Dict[RemoteSpec, AssetRemote], Dict[RemoteSpec, CASRemote]) -
-    #    The created remote instances, index first, storage last.
-    #
-    def _create_remote_instances(self, *, on_failure=None):
-        # Create a flat list of all remote specs, global or
-        # project-specific
-        remote_specs = self.global_remote_specs.copy()
-        for project in self.project_remote_specs:
-            remote_specs.extend(self.project_remote_specs[project])
-
-        # By de-duplicating it after we flattened the list, we ensure
-        # that we never instantiate the same remote twice. This
-        # de-duplication also preserves their order.
-        remote_specs = list(utils._deduplicate(remote_specs))
-
-        # Now let's create a dict of this, indexed by their specs, so
-        # that we can later assign them to the right projects.
-        index_remotes = {}
-        storage_remotes = {}
-        for remote_spec in remote_specs:
-            try:
-                index, storage = self._instantiate_remote(remote_spec)
-            except RemoteError as err:
-                if on_failure:
-                    on_failure(remote_spec, str(err))
-                    continue
-
-                raise
-
-            # Finally, we can instantiate the remote. Note that
-            # NamedTuples are hashable, so we can use them as pretty
-            # low-overhead keys.
-            if index:
-                index_remotes[remote_spec] = index
-            if storage:
-                storage_remotes[remote_spec] = storage
-
-        self._has_fetch_remotes = storage_remotes and index_remotes
-        self._has_push_remotes = any(spec.push for spec in storage_remotes) 
and any(
-            spec.push for spec in index_remotes
-        )
-
-        return index_remotes, storage_remotes
-
-    # _instantiate_remote()
-    #
-    # Instantiate a remote given its spec, asserting that it is
-    # reachable - this may produce two remote instances (a storage and
-    # an index remote as specified by the class variables).
-    #
-    # Args:
-    #
-    #    remote_spec (RemoteSpec): The spec of the remote to
-    #                              instantiate.
-    #
-    # Returns:
-    #
-    #    (Tuple[Remote|None, Remote|None]) - The remotes, index remote
-    #    first, storage remote second. One must always be specified,
-    #    the other may be None.
-    #
-    def _instantiate_remote(self, remote_spec):
-        # Our remotes can be index, storage or both. In either case,
-        # we need to use a different type of Remote for our calls, so
-        # we create two objects here
-        index = None
-        storage = None
-        if remote_spec.remote_type in [RemoteType.INDEX, RemoteType.ALL]:
-            index = AssetRemote(remote_spec)  # pylint: disable=not-callable
-            index.check()
-        if remote_spec.remote_type in [RemoteType.STORAGE, RemoteType.ALL]:
-            storage = CASRemote(remote_spec, self.cas)
-            storage.check()
-
-        return (index, storage)
-
-    # _set_remotes():
-    #
-    # Set the list of remote caches. If project is None, the global list of
-    # remote caches will be set, which is used by all projects. If a project is
-    # specified, the per-project list of remote caches will be set.
-    #
-    # Args:
-    #     remote_specs (list): List of ArtifactCacheSpec instances, in 
priority order.
-    #     project (Project): The Project instance for project-specific remotes
-    def _set_remotes(self, remote_specs, *, project=None):
-        if project is None:
-            # global remotes
-            self.global_remote_specs = remote_specs
-        else:
-            self.project_remote_specs[project] = remote_specs
+            # Check whether the specified element's project has fetch remotes
+            return bool(index_remotes and storage_remotes)
 
-    # _list_refs_mtimes()
+    # list_refs_mtimes()
     #
     # List refs in a directory, given a base path. Also returns the
     # associated mtimes
@@ -600,7 +441,7 @@ class AssetCache:
     # Returns:
     #     (iter (mtime, filename)]): iterator of tuples of mtime and refs
     #
-    def _list_refs_mtimes(self, base_path, *, glob_expr=None):
+    def list_refs_mtimes(self, base_path, *, glob_expr=None):
         path = base_path
         if glob_expr is not None:
             globdir = os.path.dirname(glob_expr)
@@ -622,7 +463,7 @@ class AssetCache:
                     # Obtain the mtime (the time a file was last modified)
                     yield (os.path.getmtime(ref_path), relative_path)
 
-    # _remove_ref()
+    # remove_ref()
     #
     # Removes a ref.
     #
@@ -636,7 +477,7 @@ class AssetCache:
     #    (AssetCacheError): If the ref didnt exist, or a system error
     #                     occurred while removing it
     #
-    def _remove_ref(self, ref):
+    def remove_ref(self, ref):
         try:
             utils._remove_path_with_parents(self._basedir, ref)
         except FileNotFoundError as e:
diff --git a/src/buildstream/_context.py b/src/buildstream/_context.py
index e5df670..7d2def6 100644
--- a/src/buildstream/_context.py
+++ b/src/buildstream/_context.py
@@ -19,6 +19,9 @@
 
 import os
 import shutil
+
+from typing import List, Dict, Set, Optional
+
 from . import utils
 from . import _site
 from . import _yaml
@@ -83,12 +86,6 @@ class Context:
         # Default root location for workspaces
         self.workspacedir = None
 
-        # specs for source cache remotes
-        self.source_cache_specs = None
-
-        # The locations from which to push and pull prebuilt artifacts
-        self.artifact_cache_specs = None
-
         # The global remote execution configuration
         self.remote_execution_specs = None
 
@@ -161,15 +158,27 @@ class Context:
         # Whether file contents are required for all artifacts in the local 
cache
         self.require_artifact_files = True
 
-        # Whether elements must be rebuilt when their dependencies have changed
-        self._strict_build_plan = None
+        # Don't shoot the messenger
+        self.messenger = Messenger()
 
         # Make sure the XDG vars are set in the environment before loading 
anything
         self._init_xdg()
 
-        self.messenger = Messenger()
-
+        #
         # Private variables
+        #
+
+        # Whether elements must be rebuilt when their dependencies have changed
+        self._strict_build_plan = None
+
+        # Lists of globally configured cache specs
+        self._global_artifact_cache_specs: List[RemoteSpec] = []
+        self._global_source_cache_specs: List[RemoteSpec] = []
+
+        # Set of all actively configured remote specs
+        self._active_artifact_cache_specs: Set[RemoteSpec] = set()
+        self._active_source_cache_specs: Set[RemoteSpec] = set()
+
         self._platform = None
         self._artifactcache = None
         self._elementsourcescache = None
@@ -327,18 +336,13 @@ class Context:
                 LoadErrorReason.INVALID_DATA,
             ) from e
 
-
         # Load artifact remote specs
-        caches = config.get_sequence("artifacts", default=[], 
allowed_types=[MappingNode])
-        for node in caches:
-            spec = RemoteSpec.new_from_node(node)
-            self.artifact_cache_specs.append(spec)
+        caches = defaults.get_sequence("artifacts", default=[], 
allowed_types=[MappingNode])
+        self._global_artifact_cache_specs = [RemoteSpec.new_from_node(node) 
for node in caches]
 
         # Load source cache remote specs
-        caches = config.get_sequence("source-caches", default=[], 
allowed_types=[MappingNode])
-        for node in caches:
-            spec = RemoteSpec.new_from_node(node)
-            self.source_cache_specs.append(spec)
+        caches = defaults.get_sequence("source-caches", default=[], 
allowed_types=[MappingNode])
+        self._global_source_cache_specs = [RemoteSpec.new_from_node(node) for 
node in caches]
 
         # Load the global remote execution config including 
pull-artifact-files setting
         remote_execution = defaults.get_mapping("remote-execution", 
default=None)
@@ -446,19 +450,6 @@ class Context:
     def add_project(self, project):
         if not self._projects:
             self._workspaces = Workspaces(project, 
self._workspace_project_cache)
-
-            #
-            # While loading the first, toplevel project, we can adjust some
-            # global settings which can be overridden on a per toplevel 
project basis.
-            #
-            override_node = self.get_overrides(project.name)
-            if override_node:
-                remote_execution = 
override_node.get_mapping("remote-execution", default=None)
-                if remote_execution:
-                    self.pull_artifact_files, self.remote_execution_specs = 
self._load_remote_execution(
-                        remote_execution
-                    )
-
         self._projects.append(project)
 
     # get_projects():
@@ -477,10 +468,116 @@ class Context:
     # invoked with as opposed to a junctioned subproject.
     #
     # Returns:
-    #    (Project): The Project object
+    #    (Project): The toplevel Project object, or None
     #
     def get_toplevel_project(self):
-        return self._projects[0]
+        try:
+            return self._projects[0]
+        except IndexError:
+            return None
+
+    # initialize_remotes()
+    #
+    # This will resolve what remotes each loaded project will interact
+    # with an initialize the underlying asset cache modules.
+    #
+    # Note that this can be called more than once, in the case that
+    # Stream() has loaded additional projects during the load cycle
+    # and some state needs to be recalculated.
+    #
+    # Args:
+    #    connect_artifact_cache: Whether to try to contact remote artifact 
caches
+    #    connect_source_cache: Whether to try to contact remote source caches
+    #    artifact_remote: An overriding artifact cache remote, or None
+    #    source_remote: An overriding source cache remote, or None
+    #
+    def initialize_remotes(
+        self,
+        connect_artifact_cache: bool,
+        connect_source_cache: bool,
+        artifact_remote: Optional[RemoteSpec],
+        source_remote: Optional[RemoteSpec],
+    ) -> None:
+
+        # Ensure all projects are fully loaded.
+        for project in self._projects:
+            project.ensure_fully_loaded()
+
+        #
+        # If the global remote execution specs have been overridden by the
+        # toplevel project, then adjust them now that we're all loaded.
+        #
+        project = self.get_toplevel_project()
+        if project:
+            override_node = self.get_overrides(project.name)
+            if override_node:
+                remote_execution = 
override_node.get_mapping("remote-execution", default=None)
+                if remote_execution:
+                    self.pull_artifact_files, self.remote_execution_specs = 
self._load_remote_execution(
+                        remote_execution
+                    )
+
+        # Collect a table of which specs apply to each project, these
+        # are calculated here and handed over to the asset caches.
+        #
+        project_artifact_cache_specs: Dict[str, List[RemoteSpec]] = {}
+        project_source_cache_specs: Dict[str, List[RemoteSpec]] = {}
+
+        cli_artifact_remotes = [artifact_remote] if artifact_remote else []
+        cli_source_remotes = [source_remote] if source_remote else []
+
+        #
+        # Maintain our list of remote specs for artifact and source caches
+        #
+        for project in self._projects:
+
+            artifact_specs: List[RemoteSpec] = []
+            source_specs: List[RemoteSpec] = []
+
+            override_node = self.get_overrides(project.name)
+
+            # Resolve which remote specs to use, CLI -> Override -> Global -> 
Project recommendation
+            if connect_artifact_cache:
+                caches = override_node.get_sequence("artifacts", default=[], 
allowed_types=[MappingNode])
+                override_artifact_specs: List[RemoteSpec] = 
[RemoteSpec.new_from_node(node) for node in caches]
+                artifact_specs = (
+                    cli_artifact_remotes
+                    or override_artifact_specs
+                    or self._global_artifact_cache_specs
+                    or project.artifact_cache_specs
+                )
+                artifact_specs = list(utils._deduplicate(artifact_specs))
+
+            if connect_source_cache:
+                caches = override_node.get_sequence("source-caches", 
default=[], allowed_types=[MappingNode])
+                override_source_specs: List[RemoteSpec] = 
[RemoteSpec.new_from_node(node) for node in caches]
+                source_specs = (
+                    cli_source_remotes
+                    or override_source_specs
+                    or self._global_source_cache_specs
+                    or project.source_cache_specs
+                )
+                source_specs = list(utils._deduplicate(source_specs))
+
+            # Store them for lookups later on
+            project_artifact_cache_specs[project.name] = artifact_specs
+            project_source_cache_specs[project.name] = source_specs
+
+            #
+            # Now that we know which remote specs are going to be used, 
maintain
+            # our total set of overall active remote specs, this helps the 
asset cache
+            # modules to maintain a remote connection for the required remotes.
+            #
+            for spec in artifact_specs:
+                self._active_artifact_cache_specs.add(spec)
+            for spec in source_specs:
+                self._active_source_cache_specs.add(spec)
+
+        # Now initialize the underlying asset caches
+        #
+        self.artifactcache.setup_remotes(self._active_artifact_cache_specs, 
project_artifact_cache_specs)
+        
self.elementsourcescache.setup_remotes(self._active_source_cache_specs, 
project_source_cache_specs)
+        self.sourcecache.setup_remotes(self._active_source_cache_specs, 
project_source_cache_specs)
 
     # get_workspaces():
     #
@@ -542,6 +639,10 @@ class Context:
     # does not require file contents of all artifacts to be available in the
     # local cache.
     #
+    # XXX FIXME: This API can be removed, it is determined by configuration
+    #            belonging to the Context anyway.
+    #
+    #
     def set_artifact_files_optional(self):
         self.require_artifact_files = False
 
diff --git a/src/buildstream/_elementsourcescache.py 
b/src/buildstream/_elementsourcescache.py
index 873806e..c80ad4b 100644
--- a/src/buildstream/_elementsourcescache.py
+++ b/src/buildstream/_elementsourcescache.py
@@ -32,10 +32,6 @@ REMOTE_ASSET_SOURCE_URN_TEMPLATE = 
"urn:fdc:buildstream.build:2020:source:{}"
 #    context (Context): The Buildstream context
 #
 class ElementSourcesCache(AssetCache):
-
-    spec_name = "source_cache_specs"
-    config_node_name = "source-caches"
-
     def __init__(self, context):
         super().__init__(context)
 
@@ -86,11 +82,13 @@ class ElementSourcesCache(AssetCache):
 
         uri = REMOTE_ASSET_SOURCE_URN_TEMPLATE.format(ref)
 
+        index_remotes, storage_remotes = self.get_remotes(project.name, False)
+
         source_digest = None
         errors = []
         # Start by pulling our source proto, so that we know which
         # blobs to pull
-        for remote in self._index_remotes[project]:
+        for remote in index_remotes:
             remote.init()
             try:
                 plugin.status("Pulling source {} <- {}".format(display_key, 
remote))
@@ -114,7 +112,7 @@ class ElementSourcesCache(AssetCache):
             return False
 
         errors = []
-        for remote in self._storage_remotes[project]:
+        for remote in storage_remotes:
             remote.init()
             try:
                 plugin.status("Pulling data for source {} <- 
{}".format(display_key, remote))
@@ -160,8 +158,7 @@ class ElementSourcesCache(AssetCache):
 
         uri = REMOTE_ASSET_SOURCE_URN_TEMPLATE.format(ref)
 
-        index_remotes = [r for r in self._index_remotes[project] if 
r.spec.push]
-        storage_remotes = [r for r in self._storage_remotes[project] if 
r.spec.push]
+        index_remotes, storage_remotes = self.get_remotes(project.name, True)
 
         source_proto = self.load_proto(sources)
         source_digest = 
self.cas.add_object(buffer=source_proto.SerializeToString())
diff --git a/src/buildstream/_sourcecache.py b/src/buildstream/_sourcecache.py
index 475a166..2284d0d 100644
--- a/src/buildstream/_sourcecache.py
+++ b/src/buildstream/_sourcecache.py
@@ -36,10 +36,6 @@ REMOTE_ASSET_SOURCE_URN_TEMPLATE = 
"urn:fdc:buildstream.build:2020:source:{}"
 #    context (Context): The Buildstream context
 #
 class SourceCache(AssetCache):
-
-    spec_name = "source_cache_specs"
-    config_node_name = "source-caches"
-
     def __init__(self, context):
         super().__init__(context)
 
@@ -117,8 +113,7 @@ class SourceCache(AssetCache):
         project = source._get_project()
         display_key = source._get_brief_display_key()
 
-        index_remotes = self._index_remotes[project]
-        storage_remotes = self._storage_remotes[project]
+        index_remotes, storage_remotes = self.get_remotes(project.name, False)
 
         # First fetch the source directory digest so we know what to pull
         source_digest = None
@@ -172,13 +167,7 @@ class SourceCache(AssetCache):
         ref = source._get_source_name()
         project = source._get_project()
 
-        index_remotes = []
-        storage_remotes = []
-
-        # find configured push remotes for this source
-        if self._has_push_remotes:
-            index_remotes = [r for r in self._index_remotes[project] if 
r.spec.push]
-            storage_remotes = [r for r in self._storage_remotes[project] if 
r.spec.push]
+        index_remotes, storage_remotes = self.get_remotes(project.name, True)
 
         pushed_storage = False
         pushed_index = False
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index fa2421e..1728bd7 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -47,6 +47,7 @@ from ._scheduler import (
 from .element import Element
 from ._profile import Topics, PROFILER
 from ._project import ProjectRefStorage
+from ._remotespec import RemoteType, RemoteSpec
 from ._state import State
 from .types import _KeyStrength, _PipelineSelection, _Scope
 from .plugin import Plugin
@@ -137,7 +138,7 @@ class Stream:
     #    targets (list of str): Targets to pull
     #    selection (_PipelineSelection): The selection mode for the specified 
targets
     #    except_targets (list of str): Specified targets to except from 
fetching
-    #    use_artifact_config (bool): If artifact remote configs should be 
loaded
+    #    connect_artifact_cache (bool): Whether to try to contact remote 
artifact caches
     #    load_artifacts (bool): Whether to load artifacts with artifact names
     #
     # Returns:
@@ -148,7 +149,7 @@ class Stream:
         *,
         selection=_PipelineSelection.NONE,
         except_targets=(),
-        use_artifact_config=False,
+        connect_artifact_cache=False,
         load_artifacts=False,
     ):
         with PROFILER.profile(Topics.LOAD_SELECTION, 
"_".join(t.replace(os.sep, "-") for t in targets)):
@@ -156,7 +157,7 @@ class Stream:
                 targets,
                 selection=selection,
                 except_targets=except_targets,
-                use_artifact_config=use_artifact_config,
+                connect_artifact_cache=connect_artifact_cache,
                 load_artifacts=load_artifacts,
             )
 
@@ -200,7 +201,7 @@ class Stream:
         else:
             selection = _PipelineSelection.BUILD if scope == _Scope.BUILD else 
_PipelineSelection.RUN
 
-            elements = self.load_selection((element,), selection=selection, 
use_artifact_config=True)
+            elements = self.load_selection((element,), selection=selection, 
connect_artifact_cache=True)
 
             # Get element to stage from `targets` list.
             # If scope is BUILD, it will not be in the `elements` list.
@@ -269,17 +270,13 @@ class Stream:
     #
     def build(self, targets, *, selection=_PipelineSelection.PLAN, 
ignore_junction_targets=False, remote=None):
 
-        use_config = True
-        if remote:
-            use_config = False
-
         elements = self._load(
             targets,
             selection=selection,
             ignore_junction_targets=ignore_junction_targets,
-            use_artifact_config=use_config,
+            connect_artifact_cache=True,
             artifact_remote_url=remote,
-            use_source_config=True,
+            connect_source_cache=True,
             dynamic_plan=True,
         )
 
@@ -330,15 +327,11 @@ class Stream:
     #
     def fetch(self, targets, *, selection=_PipelineSelection.PLAN, 
except_targets=None, remote=None):
 
-        use_source_config = True
-        if remote:
-            use_source_config = False
-
         elements = self._load(
             targets,
             selection=selection,
             except_targets=except_targets,
-            use_source_config=use_source_config,
+            connect_source_cache=True,
             source_remote_url=remote,
         )
 
@@ -394,16 +387,8 @@ class Stream:
     #
     def source_push(self, targets, *, selection=_PipelineSelection.NONE, 
remote=None):
 
-        use_source_config = True
-        if remote:
-            use_source_config = False
-
         elements = self._load(
-            targets,
-            selection=selection,
-            use_source_config=use_source_config,
-            source_remote_url=remote,
-            load_artifacts=True,
+            targets, selection=selection, connect_source_cache=True, 
source_remote_url=remote, load_artifacts=True,
         )
 
         if not self._sourcecache.has_push_remotes():
@@ -433,15 +418,11 @@ class Stream:
     #
     def pull(self, targets, *, selection=_PipelineSelection.NONE, 
ignore_junction_targets=False, remote=None):
 
-        use_config = True
-        if remote:
-            use_config = False
-
         elements = self._load(
             targets,
             selection=selection,
             ignore_junction_targets=ignore_junction_targets,
-            use_artifact_config=use_config,
+            connect_artifact_cache=True,
             artifact_remote_url=remote,
             load_artifacts=True,
             attempt_artifact_metadata=True,
@@ -475,15 +456,11 @@ class Stream:
     #
     def push(self, targets, *, selection=_PipelineSelection.NONE, 
ignore_junction_targets=False, remote=None):
 
-        use_config = True
-        if remote:
-            use_config = False
-
         elements = self._load(
             targets,
             selection=selection,
             ignore_junction_targets=ignore_junction_targets,
-            use_artifact_config=use_config,
+            connect_artifact_cache=True,
             artifact_remote_url=remote,
             load_artifacts=True,
         )
@@ -536,7 +513,7 @@ class Stream:
         elements = self._load(
             (target,),
             selection=selection,
-            use_artifact_config=True,
+            connect_artifact_cache=True,
             load_artifacts=True,
             attempt_artifact_metadata=True,
         )
@@ -624,7 +601,7 @@ class Stream:
     def artifact_show(self, targets, *, selection=_PipelineSelection.NONE):
         # Obtain list of Element and/or ArtifactElement objects
         target_objects = self.load_selection(
-            targets, selection=selection, use_artifact_config=True, 
load_artifacts=True
+            targets, selection=selection, connect_artifact_cache=True, 
load_artifacts=True
         )
 
         if self._artifacts.has_fetch_remotes():
@@ -1255,38 +1232,6 @@ class Stream:
 
         return elements, except_elements, artifacts
 
-    # _connect_remotes()
-    #
-    # Connect to the source and artifact remotes.
-    #
-    # Args:
-    #     artifact_url: The url of the artifact server to connect to.
-    #     source_url: The url of the source server to connect to.
-    #     use_artifact_config: Whether to use the artifact config.
-    #     use_source_config: Whether to use the source config.
-    #     reinitialize: Whether to reinitialize from scratch
-    #
-    def _connect_remotes(
-        self,
-        artifact_url: str,
-        source_url: str,
-        use_artifact_config: bool,
-        use_source_config: bool,
-        reinitialize: bool = False,
-    ):
-        # ArtifactCache.setup_remotes expects all projects to be fully loaded
-        for project in self._context.get_projects():
-            project.ensure_fully_loaded()
-
-        # Connect to remote caches, this needs to be done before resolving 
element state
-        self._artifacts.setup_remotes(
-            use_config=use_artifact_config, remote_url=artifact_url, 
reinitialize=reinitialize
-        )
-        self._elementsourcescache.setup_remotes(
-            use_config=use_source_config, remote_url=source_url, 
reinitialize=reinitialize
-        )
-        self._sourcecache.setup_remotes(use_config=use_source_config, 
remote_url=source_url, reinitialize=reinitialize)
-
     # _resolve_cached_remotely()
     #
     # Checks whether the listed elements are currently cached in
@@ -1411,8 +1356,8 @@ class Stream:
     #    selection (_PipelineSelection): The selection mode for the specified 
targets
     #    except_targets (list of str): Specified targets to except from 
fetching
     #    ignore_junction_targets (bool): Whether junction targets should be 
filtered out
-    #    use_artifact_config (bool): Whether to initialize artifacts with the 
config
-    #    use_source_config (bool): Whether to initialize remote source caches 
with the config
+    #    connect_artifact_cache (bool): Whether to try to contact remote 
artifact caches
+    #    connect_source_cache (bool): Whether to try to contact remote source 
caches
     #    artifact_remote_url (str): A remote url for initializing the artifacts
     #    source_remote_url (str): A remote url for initializing source caches
     #    dynamic_plan (bool): Require artifacts as needed during the build
@@ -1430,8 +1375,8 @@ class Stream:
         selection=_PipelineSelection.NONE,
         except_targets=(),
         ignore_junction_targets=False,
-        use_artifact_config=False,
-        use_source_config=False,
+        connect_artifact_cache=False,
+        connect_source_cache=False,
         artifact_remote_url=None,
         source_remote_url=None,
         dynamic_plan=False,
@@ -1455,8 +1400,20 @@ class Stream:
         # Hold on to the targets
         self.targets = elements
 
+        # FIXME: Instead of converting the URL to a RemoteSpec here, the CLI 
needs to
+        #        be enhanced to parse a fully qualified RemoteSpec (including 
certs etc)
+        #        from the command line, the CLI should be feeding the 
RemoteSpec through
+        #        the Stream API directly.
+        #
+        artifact_remote = None
+        if artifact_remote_url:
+            artifact_remote = RemoteSpec(RemoteType.ALL, artifact_remote_url, 
push=True)
+        source_remote = None
+        if source_remote_url:
+            source_remote = RemoteSpec(RemoteType.ALL, source_remote_url, 
push=True)
+
         # Connect to remote caches, this needs to be done before resolving 
element state
-        self._connect_remotes(artifact_remote_url, source_remote_url, 
use_artifact_config, use_source_config)
+        self._context.initialize_remotes(connect_artifact_cache, 
connect_source_cache, artifact_remote, source_remote)
 
         # In some cases we need to have an actualized artifact, with all of
         # it's metadata, such that we can derive attributes about the artifact
@@ -1482,14 +1439,12 @@ class Stream:
                 artifact_targets, [], rewritable=False, 
valid_artifact_names=True
             )
 
-            # FIXME:
-            #
-            #    Sadly, we need to reinitialize just because we 
re-instantiated new projects due to
-            #    downloading artifacts - this could be fixed by addressing the 
awkward structure
-            #    of remotes in the asset caches.
+            # It can be that new remotes have been added by way of loading new
+            # projects referenced by the new artifact elements, so we need to
+            # ensure those remotes are also initialized.
             #
-            self._connect_remotes(
-                artifact_remote_url, source_remote_url, use_artifact_config, 
use_source_config, reinitialize=True
+            self._context.initialize_remotes(
+                connect_artifact_cache, connect_source_cache, artifact_remote, 
source_remote
             )
 
         self.targets += artifacts
diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index 503361e..c8c5807 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -1959,6 +1959,7 @@ class Element(Plugin):
     #   (bool): True if this element does not need a push job to be created
     #
     def _skip_push(self, *, skip_uncached):
+
         if not self.__artifacts.has_push_remotes(plugin=self):
             # No push remotes for this element's project
             return True
diff --git a/tests/artifactcache/capabilities.py 
b/tests/artifactcache/capabilities.py
index 0f2f1aa..a74ed6c 100644
--- a/tests/artifactcache/capabilities.py
+++ b/tests/artifactcache/capabilities.py
@@ -40,8 +40,8 @@ def 
test_artifact_cache_with_missing_capabilities_is_skipped(cli, tmpdir, datafi
             # Create a local artifact cache handle
             artifactcache = context.artifactcache
 
-            # Manually setup the CAS remote
-            artifactcache.setup_remotes(use_config=True)
+            # Initialize remotes
+            context.initialize_remotes(True, True, None, None)
 
             assert (
                 not artifactcache.has_fetch_remotes()
diff --git a/tests/artifactcache/config.py b/tests/artifactcache/config.py
index 1b8cbad..3bf853a 100644
--- a/tests/artifactcache/config.py
+++ b/tests/artifactcache/config.py
@@ -1,13 +1,11 @@
 # Pylint doesn't play well with fixtures and dependency injection from pytest
 # pylint: disable=redefined-outer-name
 
-import itertools
 import os
 
 import pytest
 
 from buildstream._remotespec import RemoteSpec, RemoteType
-from buildstream._artifactcache import ArtifactCache
 from buildstream._project import Project
 from buildstream.utils import _deduplicate
 from buildstream import _yaml
@@ -102,11 +100,13 @@ def test_artifact_cache_precedence(tmpdir, 
override_caches, project_caches, user
         project = Project(str(project_dir), context)
         project.ensure_fully_loaded()
 
-        # Use the helper from the artifactcache module to parse our 
configuration.
-        parsed_cache_specs = 
ArtifactCache._configured_remote_cache_specs(context, project)
+        # Check the specs which the artifact cache thinks are configured
+        context.initialize_remotes(True, True, None, None)
+        artifactcache = context.artifactcache
+        parsed_cache_specs = artifactcache._project_specs[project.name]
 
         # Verify that it was correctly read.
-        expected_cache_specs = 
list(_deduplicate(itertools.chain(override_caches, project_caches, 
user_caches)))
+        expected_cache_specs = list(_deduplicate(override_caches or 
user_caches or project_caches))
         assert parsed_cache_specs == expected_cache_specs
 
 
@@ -220,8 +220,10 @@ def test_paths_for_artifact_config_are_expanded(tmpdir, 
monkeypatch, artifacts_c
         project = Project(str(project_dir), context)
         project.ensure_fully_loaded()
 
-        # Use the helper from the artifactcache module to parse our 
configuration.
-        parsed_cache_specs = 
ArtifactCache._configured_remote_cache_specs(context, project)
+        # Check the specs which the artifact cache thinks are configured
+        context.initialize_remotes(True, True, None, None)
+        artifactcache = context.artifactcache
+        parsed_cache_specs = artifactcache._project_specs[project.name]
 
     if isinstance(artifacts_config, dict):
         artifacts_config = [artifacts_config]
diff --git a/tests/artifactcache/pull.py b/tests/artifactcache/pull.py
index fc27e75..502c350 100644
--- a/tests/artifactcache/pull.py
+++ b/tests/artifactcache/pull.py
@@ -86,8 +86,8 @@ def test_pull(cli, tmpdir, datafiles):
             # Create a local artifact cache handle
             artifactcache = context.artifactcache
 
-            # Manually setup the CAS remote
-            artifactcache.setup_remotes(use_config=True)
+            # 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"
@@ -138,9 +138,10 @@ def test_pull_tree(cli, tmpdir, datafiles):
             # 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
-            # Manually setup the CAS remote
-            artifactcache.setup_remotes(use_config=True)
             assert artifactcache.has_push_remotes()
 
             directory = remote_execution_pb2.Directory()
@@ -161,7 +162,7 @@ def test_pull_tree(cli, tmpdir, datafiles):
             cli.remove_artifact_from_cache(project_dir, "target.bst")
 
             # Assert that we are not cached locally anymore
-            artifactcache.close_grpc_channels()
+            artifactcache.release_resources()
             cas._casd_channel.request_shutdown()
             cas.close_grpc_channels()
             assert cli.get_element_state(project_dir, "target.bst") != "cached"
diff --git a/tests/artifactcache/push.py b/tests/artifactcache/push.py
index 02ff259..48985df 100644
--- a/tests/artifactcache/push.py
+++ b/tests/artifactcache/push.py
@@ -39,9 +39,8 @@ def _push(cli, cache_dir, project_dir, config_file, target):
         for e in element._dependencies(_Scope.ALL):
             e._initialize_state()
 
-        # Manually setup the CAS remotes
-        artifactcache.setup_remotes(use_config=True)
-        artifactcache.initialize_remotes()
+        # Initialize remotes
+        context.initialize_remotes(True, True, None, None)
 
         assert artifactcache.has_push_remotes(plugin=element), "No remote 
configured for element target.bst"
         assert element._push(), "Push operation failed"
@@ -139,9 +138,8 @@ def test_push_message(tmpdir, datafiles):
             # Create a local artifact cache handle
             artifactcache = context.artifactcache
 
-            # Manually setup the artifact remote
-            artifactcache.setup_remotes(use_config=True)
-            artifactcache.initialize_remotes()
+            # Initialize remotes
+            context.initialize_remotes(True, True, None, None)
             assert artifactcache.has_push_remotes()
 
             command = remote_execution_pb2.Command(
diff --git a/tests/frontend/pull.py b/tests/frontend/pull.py
index b721ca8..6733584 100644
--- a/tests/frontend/pull.py
+++ b/tests/frontend/pull.py
@@ -391,18 +391,14 @@ def test_build_remote_option(caplog, cli, tmpdir, 
datafiles):
 
     with create_artifact_share(os.path.join(str(tmpdir), "artifactshare1")) as 
shareuser, create_artifact_share(
         os.path.join(str(tmpdir), "artifactshare2")
-    ) as shareproject, create_artifact_share(os.path.join(str(tmpdir), 
"artifactshare3")) as sharecli:
-
-        # Add shareproject repo url to project.conf
-        with open(os.path.join(project, "project.conf"), "a") as projconf:
-            projconf.write("artifacts:\n- url: {}\n  push: 
True".format(shareproject.repo))
+    ) as sharecli:
 
         # Configure shareuser remote in user conf
         cli.configure({"artifacts": [{"url": shareuser.repo, "push": True}]})
 
-        # Push the artifacts to the shareuser and shareproject remotes.
-        # Assert that shareuser and shareproject have the artfifacts cached,
-        # but sharecli doesn't, then delete locally cached elements
+        # Push the artifacts to the shareuser remote.
+        # Assert that shareuser has the artfifacts cached, but sharecli 
doesn't,
+        # then delete locally cached elements
         result = cli.run(project=project, args=["build", "target.bst"])
         result.assert_success()
         all_elements = ["target.bst", "import-bin.bst", "compose-all.bst"]
@@ -410,11 +406,10 @@ def test_build_remote_option(caplog, cli, tmpdir, 
datafiles):
             assert element_name in result.get_pushed_elements()
             assert_not_shared(cli, sharecli, project, element_name)
             assert_shared(cli, shareuser, project, element_name)
-            assert_shared(cli, shareproject, project, element_name)
             cli.remove_artifact_from_cache(project, element_name)
 
         # Now check that a build with cli set as sharecli results in nothing 
being pulled,
-        # as it doesn't have them cached and shareuser/shareproject should be 
ignored. This
+        # as it doesn't have them cached and shareuser should be ignored. This
         # will however result in the artifacts being built and pushed to it
         result = cli.run(project=project, args=["build", "--remote", 
sharecli.repo, "target.bst"])
         result.assert_success()
@@ -430,7 +425,6 @@ def test_build_remote_option(caplog, cli, tmpdir, 
datafiles):
         for element_name in all_elements:
             assert cli.get_element_state(project, element_name) == "cached"
             assert element_name in result.get_pulled_elements()
-        assert shareproject.repo not in result.stderr
         assert shareuser.repo not in result.stderr
         assert sharecli.repo in result.stderr
 
diff --git a/tests/sourcecache/capabilities.py 
b/tests/sourcecache/capabilities.py
index 964b458..67be749 100644
--- a/tests/sourcecache/capabilities.py
+++ b/tests/sourcecache/capabilities.py
@@ -33,12 +33,12 @@ def 
test_artifact_cache_with_missing_capabilities_is_skipped(cli, tmpdir, datafi
             project = Project(project_dir, context)
             project.ensure_fully_loaded()
 
+            # Initialize remotes
+            context.initialize_remotes(True, True, None, None)
+
             # Create a local artifact cache handle
             sourcecache = context.sourcecache
 
-            # Manually setup the CAS remote
-            sourcecache.setup_remotes(use_config=True)
-
             assert (
                 not sourcecache.has_fetch_remotes()
             ), "System didn't realize the source cache didn't support 
BuildStream"

Reply via email to