Fix mixed canonical/non-canonical project merge

When defining multiple project stanzas for the same project the
resulting job graphs get merged. This should also work if they are
given as a mixture of canonical and non-canonical project names like
the following stanza.

- project:
    name: review.example.com/org/project1
    check:
      jobs:
        - common-config-job

- project:
    name: org/project1
    check:
      jobs:
        - project1-job

However currently only one of the stanzas are effective depending on
which was processed last.

This can be fixed by canonicalizing the project names when extending
the unparsed config.

Change-Id: Icaf8fca3aa4577b009d691f9a67adcb43ea040f5
diff --git a/tests/fixtures/config/tenant-parser/git/common-config/zuul.yaml b/tests/fixtures/config/tenant-parser/git/common-config/zuul.yaml
index e21f967..a28ef54 100644
--- a/tests/fixtures/config/tenant-parser/git/common-config/zuul.yaml
+++ b/tests/fixtures/config/tenant-parser/git/common-config/zuul.yaml
@@ -18,8 +18,10 @@
 - job:
     name: common-config-job
 
+# Use the canonical name here. This should be merged with the org/project1 in
+# the other repo.
 - project:
-    name: org/project1
+    name: review.example.com/org/project1
     check:
       jobs:
         - common-config-job
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 99f10f6..dacfa53 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -1161,8 +1161,8 @@
                                                   tenant.config_projects,
                                                   tenant.untrusted_projects,
                                                   cached, tenant)
-        unparsed_config.extend(tenant.config_projects_config)
-        unparsed_config.extend(tenant.untrusted_projects_config)
+        unparsed_config.extend(tenant.config_projects_config, tenant=tenant)
+        unparsed_config.extend(tenant.untrusted_projects_config, tenant=tenant)
         tenant.layout = TenantParser._parseLayout(base, tenant,
                                                   unparsed_config,
                                                   scheduler,
diff --git a/zuul/model.py b/zuul/model.py
index b027c53..7607dc2 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -2360,14 +2360,25 @@
         r.semaphores = copy.deepcopy(self.semaphores)
         return r
 
-    def extend(self, conf):
+    def extend(self, conf, tenant=None):
         if isinstance(conf, UnparsedTenantConfig):
             self.pragmas.extend(conf.pragmas)
             self.pipelines.extend(conf.pipelines)
             self.jobs.extend(conf.jobs)
             self.project_templates.extend(conf.project_templates)
             for k, v in conf.projects.items():
-                self.projects.setdefault(k, []).extend(v)
+                name = k
+                # If we have the tenant add the projects to
+                # the according canonical name instead of the given project
+                # name. If it is not found, it's ok to add this to the given
+                # name. We also don't need to throw the
+                # ProjectNotFoundException here as semantic validation occurs
+                # later where it will fail then.
+                if tenant is not None:
+                    trusted, project = tenant.getProject(k)
+                    if project is not None:
+                        name = project.canonical_name
+                self.projects.setdefault(name, []).extend(v)
             self.nodesets.extend(conf.nodesets)
             self.secrets.extend(conf.secrets)
             self.semaphores.extend(conf.semaphores)