Add source to project and remove unused tenant attrs

This adds to the project class a reference to the source which
created it.  This forms a cycle with connection->project->source,
but we expect these objects to all have similar lifecycles (they
are likely to persist through the lifetime of a tenant, but may
be replaced during a full reconfiguration).

This lets us drop the (source, project) tuples we were carrying
around on the Tenant class which simplifies it.

Change-Id: I5e72255815131493c516a84463dcfd7100f4e827
Story: 2000953
diff --git a/tests/unit/test_git_driver.py b/tests/unit/test_git_driver.py
index 4d75944..9c7ebb1 100644
--- a/tests/unit/test_git_driver.py
+++ b/tests/unit/test_git_driver.py
@@ -27,10 +27,10 @@
         tenant = self.sched.abide.tenants.get('tenant-one')
         # Check that we have the git source for common-config and the
         # gerrit source for the project.
-        self.assertEqual('git', tenant.config_repos[0][0].name)
-        self.assertEqual('common-config', tenant.config_repos[0][1].name)
-        self.assertEqual('gerrit', tenant.project_repos[0][0].name)
-        self.assertEqual('org/project', tenant.project_repos[0][1].name)
+        self.assertEqual('git', tenant.config_repos[0].source.name)
+        self.assertEqual('common-config', tenant.config_repos[0].name)
+        self.assertEqual('gerrit', tenant.project_repos[0].source.name)
+        self.assertEqual('org/project', tenant.project_repos[0].name)
 
         # The configuration for this test is accessed via the git
         # driver (in common-config), rather than the gerrit driver, so
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index 7269473..a40054f 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -38,12 +38,11 @@
         super(TestJob, self).setUp()
         self.connection = Dummy(connection_name='dummy_connection')
         self.source = Dummy(canonical_hostname='git.example.com',
-                            name='dummy_connection',  # TODOv3(jeblair): remove
                             connection=self.connection)
         self.tenant = model.Tenant('tenant')
         self.layout = model.Layout()
         self.project = model.Project('project', self.source)
-        self.tenant.addProjectRepo(self.source, self.project)
+        self.tenant.addProjectRepo(self.project)
         self.pipeline = model.Pipeline('gate', self.layout)
         self.layout.addPipeline(self.pipeline)
         self.queue = model.ChangeQueue(self.pipeline)
@@ -792,7 +791,7 @@
                         connection=connection1)
 
         source1_project1 = model.Project('project1', source1)
-        tenant.addConfigRepo(source1, source1_project1)
+        tenant.addConfigRepo(source1_project1)
         d = {'project1':
              {'git1.example.com': source1_project1}}
         self.assertEqual(d, tenant.projects)
@@ -802,7 +801,7 @@
                          tenant.getProject('git1.example.com/project1'))
 
         source1_project2 = model.Project('project2', source1)
-        tenant.addProjectRepo(source1, source1_project2)
+        tenant.addProjectRepo(source1_project2)
         d = {'project1':
              {'git1.example.com': source1_project1},
              'project2':
@@ -819,7 +818,7 @@
                         connection=connection2)
 
         source2_project1 = model.Project('project1', source2)
-        tenant.addProjectRepo(source2, source2_project1)
+        tenant.addProjectRepo(source2_project1)
         d = {'project1':
              {'git1.example.com': source1_project1,
               'git2.example.com': source2_project1},
@@ -838,7 +837,7 @@
                          tenant.getProject('git2.example.com/project1'))
 
         source2_project2 = model.Project('project2', source2)
-        tenant.addConfigRepo(source2, source2_project2)
+        tenant.addConfigRepo(source2_project2)
         d = {'project1':
              {'git1.example.com': source1_project1,
               'git2.example.com': source2_project1},
@@ -864,7 +863,7 @@
                          tenant.getProject('git2.example.com/project2'))
 
         source1_project2b = model.Project('subpath/project2', source1)
-        tenant.addConfigRepo(source1, source1_project2b)
+        tenant.addConfigRepo(source1_project2b)
         d = {'project1':
              {'git1.example.com': source1_project1,
               'git2.example.com': source2_project1},
@@ -885,7 +884,7 @@
             tenant.getProject('git1.example.com/subpath/project2'))
 
         source2_project2b = model.Project('subpath/project2', source2)
-        tenant.addConfigRepo(source2, source2_project2b)
+        tenant.addConfigRepo(source2_project2b)
         d = {'project1':
              {'git1.example.com': source1_project1,
               'git2.example.com': source2_project1},
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 5009ca6..4e08c13 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -772,10 +772,10 @@
         config_repos, project_repos = \
             TenantParser._loadTenantConfigRepos(
                 project_key_dir, connections, conf)
-        for source, repo in config_repos:
-            tenant.addConfigRepo(source, repo)
-        for source, repo in project_repos:
-            tenant.addProjectRepo(source, repo)
+        for repo in config_repos:
+            tenant.addConfigRepo(repo)
+        for repo in project_repos:
+            tenant.addProjectRepo(repo)
         tenant.config_repos_config, tenant.project_repos_config = \
             TenantParser._loadTenantInRepoLayouts(merger, connections,
                                                   tenant.config_repos,
@@ -848,13 +848,13 @@
                 project = source.getProject(conf_repo)
                 TenantParser._loadProjectKeys(
                     project_key_dir, source_name, project)
-                config_repos.append((source, project))
+                config_repos.append(project)
 
             for conf_repo in conf_source.get('project-repos', []):
                 project = source.getProject(conf_repo)
                 TenantParser._loadProjectKeys(
                     project_key_dir, source_name, project)
-                project_repos.append((source, project))
+                project_repos.append(project)
 
         return config_repos, project_repos
 
@@ -865,7 +865,7 @@
         project_repos_config = model.UnparsedTenantConfig()
         jobs = []
 
-        for (source, project) in config_repos:
+        for project in config_repos:
             # If we have cached data (this is a reconfiguration) use it.
             if cached and project.unparsed_config:
                 TenantParser.log.info(
@@ -878,14 +878,14 @@
             project.unparsed_config = model.UnparsedTenantConfig()
             # Get main config files.  These files are permitted the
             # full range of configuration.
-            url = source.getGitUrl(project)
+            url = project.source.getGitUrl(project)
             job = merger.getFiles(project.name, url, 'master',
                                   files=['zuul.yaml', '.zuul.yaml'])
             job.source_context = model.SourceContext(project, 'master',
                                                      '', True)
             jobs.append(job)
 
-        for (source, project) in project_repos:
+        for project in project_repos:
             # If we have cached data (this is a reconfiguration) use it.
             if cached and project.unparsed_config:
                 TenantParser.log.info(
@@ -898,12 +898,12 @@
             project.unparsed_config = model.UnparsedTenantConfig()
             # Get in-project-repo config files which have a restricted
             # set of options.
-            url = source.getGitUrl(project)
+            url = project.source.getGitUrl(project)
             # For each branch in the repo, get the zuul.yaml for that
             # branch.  Remember the branch and then implicitly add a
             # branch selector to each job there.  This makes the
             # in-repo configuration apply only to that branch.
-            for branch in source.getProjectBranches(project):
+            for branch in project.source.getProjectBranches(project):
                 project.unparsed_branch_config[branch] = \
                     model.UnparsedTenantConfig()
                 job = merger.getFiles(project.name, url, branch,
@@ -1044,13 +1044,13 @@
         new_abide.tenants[tenant.name] = new_tenant
         return new_abide
 
-    def _loadDynamicProjectData(self, config, source, project, files,
+    def _loadDynamicProjectData(self, config, project, files,
                                 config_repo):
         if config_repo:
             branches = ['master']
             fn = 'zuul.yaml'
         else:
-            branches = source.getProjectBranches(project)
+            branches = project.source.getProjectBranches(project)
             fn = '.zuul.yaml'
 
         for branch in branches:
@@ -1076,14 +1076,12 @@
     def createDynamicLayout(self, tenant, files, include_config_repos=False):
         if include_config_repos:
             config = model.UnparsedTenantConfig()
-            for source, project in tenant.config_repos:
-                self._loadDynamicProjectData(config, source, project,
-                                             files, True)
+            for project in tenant.config_repos:
+                self._loadDynamicProjectData(config, project, files, True)
         else:
             config = tenant.config_repos_config.copy()
-        for source, project in tenant.project_repos:
-            self._loadDynamicProjectData(config, source, project,
-                                         files, False)
+        for project in tenant.project_repos:
+            self._loadDynamicProjectData(config, project, files, False)
 
         layout = model.Layout()
         # NOTE: the actual pipeline objects (complete with queues and
diff --git a/zuul/model.py b/zuul/model.py
index 3585f48..64d1593 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -353,6 +353,7 @@
 
     def __init__(self, name, source, foreign=False):
         self.name = name
+        self.source = source
         self.connection_name = source.connection.connection_name
         self.canonical_hostname = source.canonical_hostname
         self.canonical_name = source.canonical_hostname + '/' + name
@@ -2503,19 +2504,13 @@
         # this tenant.
         self.unparsed_config = None
         # The list of repos from which we will read main
-        # configuration.  (source, project)
+        # configuration.
         self.config_repos = []
-        # TODOv3(jeblair): This will replace the above list but drops the
-        # source element of the tuple.
-        self._config_repos = set()
         # The unparsed config from those repos.
         self.config_repos_config = None
         # The list of projects from which we will read in-repo
-        # configuration.  (source, project)
+        # configuration.
         self.project_repos = []
-        # TODOv3(jeblair): This will replace the above list but drops the
-        # source element of the tuple.
-        self._project_repos = set()
         # The unparsed config from those repos.
         self.project_repos_config = None
         self.semaphore_handler = SemaphoreHandler()
@@ -2575,22 +2570,20 @@
                                     "with a hostname" % (name,))
         if project is None:
             return (None, None)
-        if project in self._config_repos:
+        if project in self.config_repos:
             return (True, project)
-        if project in self._project_repos:
+        if project in self.project_repos:
             return (False, project)
         # This should never happen:
         raise Exception("Project %s is neither trusted nor untrusted" %
                         (project,))
 
-    def addConfigRepo(self, source, project):
-        self.config_repos.append((source, project))
-        self._config_repos.add(project)
+    def addConfigRepo(self, project):
+        self.config_repos.append(project)
         self._addProject(project)
 
-    def addProjectRepo(self, source, project):
-        self.project_repos.append((source, project))
-        self._project_repos.add(project)
+    def addProjectRepo(self, project):
+        self.project_repos.append(project)
         self._addProject(project)