Clone repo if it is not on disk

* zuul/merger.py: Previously zuul's merger only checked an initialized
flag to determine if a repo should be cloned. If the state of the disk
changes under zuul the repo may need to be recloned even if the
initialized flag is set. Check if the repo exists on disk to determine
if the repo should be cloned to deal with external state changes. Keep
initialized flag in order to preserve ability to change git repo
settings for user and email.

* tests/test_scheduler.py: Add a test that checks a repo is recloned
when removed from the filesystem under zuul.

* tests/fixtures/layout-repo-deleted.yaml: Layout fixture for new test
added above. This creates a new repo unused by any other tests so that
failures of this test do not interfere with other tests, they will run
independent of each other.

Change-Id: I14fb34a2916002cefef73e41ec9182a073d59ef3
diff --git a/tests/fixtures/layout-repo-deleted.yaml b/tests/fixtures/layout-repo-deleted.yaml
new file mode 100644
index 0000000..967009a
--- /dev/null
+++ b/tests/fixtures/layout-repo-deleted.yaml
@@ -0,0 +1,52 @@
+pipelines:
+  - name: check
+    manager: IndependentPipelineManager
+    trigger:
+      gerrit:
+        - event: patchset-created
+    success:
+      gerrit:
+        verified: 1
+    failure:
+      gerrit:
+        verified: -1
+
+  - name: post
+    manager: IndependentPipelineManager
+    trigger:
+      gerrit:
+        - event: ref-updated
+          ref: ^(?!refs/).*$
+
+  - name: gate
+    manager: DependentPipelineManager
+    failure-message: Build failed.  For information on how to proceed, see http://wiki.example.org/Test_Failures
+    trigger:
+      gerrit:
+        - event: comment-added
+          approval:
+            - approved: 1
+    success:
+      gerrit:
+        verified: 2
+        submit: true
+    failure:
+      gerrit:
+        verified: -2
+    start:
+      gerrit:
+        verified: 0
+    precedence: high
+
+projects:
+  - name: org/delete-project
+    check:
+      - project-merge:
+        - project-test1
+        - project-test2
+    gate:
+      - project-merge:
+        - project-test1
+        - project-test2
+    post:
+      - project-post
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 70956b4..cdc6769 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -2807,6 +2807,43 @@
         self.assertEqual(A.data['status'], 'MERGED')
         self.assertEqual(A.reported, 2)
 
+    def test_repo_deleted(self):
+        self.config.set('zuul', 'layout_config',
+                        'tests/fixtures/layout-repo-deleted.yaml')
+        self.sched.reconfigure(self.config)
+
+        self.init_repo("org/delete-project")
+        A = self.fake_gerrit.addFakeChange('org/delete-project', 'master', 'A')
+
+        A.addApproval('CRVW', 2)
+        self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+        self.waitUntilSettled()
+        self.assertEqual(self.getJobFromHistory('project-merge').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test1').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test2').result,
+                         'SUCCESS')
+        self.assertEqual(A.data['status'], 'MERGED')
+        self.assertEqual(A.reported, 2)
+
+        # Delete org/new-project zuul repo. Should be recloned.
+        shutil.rmtree(os.path.join(self.git_root, "org/delete-project"))
+
+        B = self.fake_gerrit.addFakeChange('org/delete-project', 'master', 'B')
+
+        B.addApproval('CRVW', 2)
+        self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+        self.waitUntilSettled()
+        self.assertEqual(self.getJobFromHistory('project-merge').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test1').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test2').result,
+                         'SUCCESS')
+        self.assertEqual(B.data['status'], 'MERGED')
+        self.assertEqual(B.reported, 2)
+
     def test_timer(self):
         "Test that a periodic job is triggered"
         self.worker.hold_jobs_in_build = True
diff --git a/zuul/merger.py b/zuul/merger.py
index 218f7f2..5704d61 100644
--- a/zuul/merger.py
+++ b/zuul/merger.py
@@ -38,9 +38,11 @@
             self.log.exception("Unable to initialize repo for %s" % remote)
 
     def _ensure_cloned(self):
-        if self._initialized:
+        repo_is_cloned = os.path.exists(self.local_path)
+        if self._initialized and repo_is_cloned:
             return
-        if not os.path.exists(self.local_path):
+        # If the repo does not exist, clone the repo.
+        if not repo_is_cloned:
             self.log.debug("Cloning from %s to %s" % (self.remote_url,
                                                       self.local_path))
             git.Repo.clone_from(self.remote_url, self.local_path)