remove getSourceByHostname

This erroneously assumed we could only have one source with a
given hostname.  Instead, the project->source mapping in the
tenant config file is key.  Therefore, to find a source, we need
a fully qualified project name, and must use the tenant project
lookup to find the source for that project.

Change-Id: I7ab9c4222014786c14eea301a5a419b8318b9461
diff --git a/zuul/lib/connections.py b/zuul/lib/connections.py
index 262c7cb..3bed92b 100644
--- a/zuul/lib/connections.py
+++ b/zuul/lib/connections.py
@@ -35,7 +35,6 @@
 
     def __init__(self):
         self.connections = {}
-        self.sources = {}
         self.drivers = {}
 
         self.registerDriver(zuul.driver.zuul.ZuulDriver())
@@ -71,7 +70,6 @@
     def configure(self, config):
         # Register connections from the config
         connections = {}
-        sources = {}
 
         for section_name in config.sections():
             con_match = re.match(r'^connection ([\'\"]?)(.*)(\1)$',
@@ -93,9 +91,6 @@
             driver = self.drivers[con_driver]
             connection = driver.getConnection(con_name, con_config)
             connections[con_name] = connection
-            if hasattr(driver, 'getSource'):
-                source = driver.getSource(connection)
-                sources[source.canonical_hostname] = source
 
         # If the [gerrit] or [smtp] sections still exist, load them in as a
         # connection named 'gerrit' or 'smtp' respectfully
@@ -130,10 +125,6 @@
                     driver, driver.name, {})
 
         self.connections = connections
-        self.sources = sources
-
-    def getSourceByHostname(self, hostname):
-        return self.sources[hostname]
 
     def getSource(self, connection_name):
         connection = self.connections[connection_name]
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index ac5c35b..47bd471 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -486,18 +486,40 @@
         finally:
             self.layout_lock.release()
 
-    def _reenqueueGetProject(self, tenant, project):
+    def _reenqueueGetProject(self, tenant, item):
+        project = item.change.project
         # Attempt to get the same project as the one passed in.  If
         # the project is now found on a different connection, return
         # the new version of the project.  If it is no longer
         # available (due to a connection being removed), return None.
-        project_name = project.canonical_name
-        (trusted, new_project) = tenant.getProject(project_name)
+        (trusted, new_project) = tenant.getProject(project.canonical_name)
         if new_project:
             return new_project
-        source = self.connections.getSourceByHostname(
-            project.canonical_hostname)
-        return source.getProject(project.name)
+        # If this is a non-live item we may be looking at a
+        # "foreign" project, ie, one which is not defined in the
+        # config but is constructed ad-hoc to satisfy a
+        # cross-repo-dependency.  Find the corresponding live item
+        # and use its source.
+        child = item
+        while child and not child.live:
+            # This assumes that the queue does not branch behind this
+            # item, which is currently true for non-live items; if
+            # that changes, this traversal will need to be more
+            # complex.
+            if child.items_behind:
+                child = child.items_behind[0]
+            else:
+                child = None
+        if child is item:
+            return None
+        if child and child.live:
+            (child_trusted, child_project) = tenant.getProject(
+                child.change.project.canonical_name)
+            if child_project:
+                source = child_project.source
+                new_project = source.getProject(project.name)
+                return new_project
+        return None
 
     def _reenqueueTenant(self, old_tenant, tenant):
         for name, new_pipeline in tenant.layout.pipelines.items():
@@ -514,12 +536,12 @@
                 for item in shared_queue.queue:
                     if not item.item_ahead:
                         last_head = item
-                    item.item_ahead = None
-                    item.items_behind = []
                     item.pipeline = None
                     item.queue = None
                     item.change.project = self._reenqueueGetProject(
-                        tenant, item.change.project)
+                        tenant, item)
+                    item.item_ahead = None
+                    item.items_behind = []
                     if (item.change.project and
                         new_pipeline.manager.reEnqueueItem(item,
                                                            last_head)):
@@ -714,16 +736,19 @@
         event = self.trigger_event_queue.get()
         self.log.debug("Processing trigger event %s" % event)
         try:
-            source = self.connections.getSourceByHostname(
-                event.project_hostname)
-            try:
-                change = source.getChange(event)
-            except exceptions.ChangeNotFound as e:
-                self.log.debug("Unable to get change %s from "
-                               "source %s",
-                               e.change, source)
-                return
+            full_project_name = ('/'.join([event.project_hostname,
+                                           event.project_name]))
             for tenant in self.abide.tenants.values():
+                (trusted, project) = tenant.getProject(full_project_name)
+                if project is None:
+                    continue
+                try:
+                    change = project.source.getChange(event)
+                except exceptions.ChangeNotFound as e:
+                    self.log.debug("Unable to get change %s from "
+                                   "source %s",
+                                   e.change, project.source)
+                    continue
                 if (event.type == 'change-merged' and
                     hasattr(change, 'files') and
                     change.updatesConfig()):