Merge "Handle nodepool allocation failure" into feature/zuulv3
diff --git a/tests/fixtures/config/single-tenant/git/common-config/zuul.yaml b/tests/fixtures/config/single-tenant/git/common-config/zuul.yaml
index 8975fc4..e7296fc 100644
--- a/tests/fixtures/config/single-tenant/git/common-config/zuul.yaml
+++ b/tests/fixtures/config/single-tenant/git/common-config/zuul.yaml
@@ -65,6 +65,7 @@
 
 - job:
     name: project-test1
+    attempts: 4
     nodes:
       - name: controller
         image: image1
diff --git a/tests/fixtures/layout-abort-attempts.yaml b/tests/fixtures/layout-abort-attempts.yaml
deleted file mode 100644
index 86d9d78..0000000
--- a/tests/fixtures/layout-abort-attempts.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-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/).*$
-
-jobs:
-  - name: project-test1
-    attempts: 4
-
-projects:
-  - name: org/project
-    check:
-      - project-merge:
-        - project-test1
-        - project-test2
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index f65dbce..579f7a3 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -4501,9 +4501,6 @@
     def test_rerun_on_abort(self):
         "Test that if a launch server fails to run a job, it is run again"
 
-        self.config.set('zuul', 'layout_config',
-                        'tests/fixtures/layout-abort-attempts.yaml')
-        self.sched.reconfigure(self.config)
         self.launch_server.hold_jobs_in_build = True
         A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
         self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
diff --git a/tests/test_webapp.py b/tests/test_webapp.py
index 8268ef7..2211d1b 100644
--- a/tests/test_webapp.py
+++ b/tests/test_webapp.py
@@ -16,7 +16,6 @@
 # under the License.
 
 import json
-from unittest import skip
 
 from six.moves import urllib
 
@@ -33,7 +32,7 @@
         A.addApproval('code-review', 2)
         self.fake_gerrit.addEvent(A.addApproval('approved', 1))
         B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
-        A.addApproval('code-review', 2)
+        B.addApproval('code-review', 2)
         self.fake_gerrit.addEvent(B.addApproval('approved', 1))
         self.waitUntilSettled()
         self.port = self.webapp.server.socket.getsockname()[1]
@@ -69,11 +68,10 @@
             "http://localhost:%s/status/foo" % self.port)
         self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
 
-    @skip("Disabled for early v3 development")
     def test_webapp_find_change(self):
         # can we filter by change id
         req = urllib.request.Request(
-            "http://localhost:%s/status/change/1,1" % self.port)
+            "http://localhost:%s/tenant-one/status/change/1,1" % self.port)
         f = urllib.request.urlopen(req)
         data = json.loads(f.read())
 
@@ -81,7 +79,7 @@
         self.assertEqual("org/project", data[0]['project'])
 
         req = urllib.request.Request(
-            "http://localhost:%s/status/change/2,1" % self.port)
+            "http://localhost:%s/tenant-one/status/change/2,1" % self.port)
         f = urllib.request.urlopen(req)
         data = json.loads(f.read())
 
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 3f85771..a6d7cc7 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -100,6 +100,7 @@
                'irrelevant-files': to_list(str),
                'nodes': vs.Any([node], str),
                'timeout': int,
+               'attempts': int,
                '_source_project': model.Project,
                '_source_branch': vs.Any(str, None),
                }
diff --git a/zuul/model.py b/zuul/model.py
index ed7af76..2c3c7b3 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -770,7 +770,7 @@
     def addBuild(self, build):
         self.builds[build.job.name] = build
         if build.job.name not in self.tries:
-            self.tries[build.job.name] = 0
+            self.tries[build.job.name] = 1
         build.build_set = self
 
     def removeBuild(self, build):
diff --git a/zuul/webapp.py b/zuul/webapp.py
index a63f102..e16f0b4 100644
--- a/zuul/webapp.py
+++ b/zuul/webapp.py
@@ -63,7 +63,7 @@
     def stop(self):
         self.server.server_close()
 
-    def _changes_by_func(self, func):
+    def _changes_by_func(self, func, tenant_name):
         """Filter changes by a user provided function.
 
         In order to support arbitrary collection of subsets of changes
@@ -72,7 +72,7 @@
         is a flattened list of those collected changes.
         """
         status = []
-        jsonstruct = json.loads(self.cache)
+        jsonstruct = json.loads(self.cache[tenant_name])
         for pipeline in jsonstruct['pipelines']:
             for change_queue in pipeline['change_queues']:
                 for head in change_queue['heads']:
@@ -81,11 +81,11 @@
                             status.append(copy.deepcopy(change))
         return json.dumps(status)
 
-    def _status_for_change(self, rev):
+    def _status_for_change(self, rev, tenant_name):
         """Return the statuses for a particular change id X,Y."""
         def func(change):
             return change['id'] == rev
-        return self._changes_by_func(func)
+        return self._changes_by_func(func, tenant_name)
 
     def _normalize_path(self, path):
         # support legacy status.json as well as new /status
@@ -119,7 +119,7 @@
             response = webob.Response(body=self.cache[tenant_name],
                                       content_type='application/json')
         else:
-            status = self._status_for_change(path)
+            status = self._status_for_change(path, tenant_name)
             if status:
                 response = webob.Response(body=status,
                                           content_type='application/json')