Merge "Remove "properly" from error message for clarity"
diff --git a/README.rst b/README.rst
index 1b227e7..ff4d938 100644
--- a/README.rst
+++ b/README.rst
@@ -9,7 +9,7 @@
To browse the latest code, see: https://git.openstack.org/cgit/openstack-infra/zuul/tree/
To clone the latest code, use `git clone git://git.openstack.org/openstack-infra/zuul`
-Bugs are handled at: https://launchpad.net/zuul
+Bugs are handled at: https://storyboard.openstack.org/#!/project/679
Code reviews are, as you might expect, handled by gerrit. The gerrit they
use is http://review.openstack.org
diff --git a/doc/source/triggers.rst b/doc/source/triggers.rst
index dd650f2..5b745e6 100644
--- a/doc/source/triggers.rst
+++ b/doc/source/triggers.rst
@@ -34,6 +34,10 @@
be added to Gerrit. Zuul is very flexible and can take advantage of
those.
+If using Gerrit 2.7 or later, make sure the user is a member of a group
+that is granted the ``Stream Events`` permission, otherwise it will not
+be able to invoke the ``gerrit stream-events`` command over SSH.
+
Timer
-----
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 6cb5d59..36f5a42 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -462,7 +462,8 @@
This may be used for any event. It requires that a certain kind
of approval be present for the current patchset of the change (the
approval could be added by the event in question). It follows the
- same syntax as the "approval" pipeline requirement below.
+ same syntax as the :ref:`"approval" pipeline requirement below
+ <pipeline-require-approval>`.
**timer**
This trigger will run based on a cron-style time specification.
@@ -497,7 +498,8 @@
This may be used for any event. It requires that a certain kind
of approval be present for the current patchset of the change (the
approval could be added by the event in question). It follows the
- same syntax as the "approval" pipeline requirement below.
+ same syntax as the :ref:`"approval" pipeline requirement below
+ <pipeline-require-approval>`.
**require**
@@ -507,6 +509,8 @@
the conditions specified here must be met or the item will not be
enqueued.
+.. _pipeline-require-approval:
+
**approval**
This requires that a certain kind of approval be present for the
current patchset of the change (the approval could be added by the
@@ -1045,3 +1049,10 @@
If you send a SIGUSR2 to the zuul-server process, Zuul will dump a stack
trace for each running thread into its debug log. This is useful for
tracking down deadlock or otherwise slow threads.
+
+When `yappi <https://code.google.com/p/yappi/>`_ (Yet Another Python
+Profiler) is available, additional functions' and threads' stats are
+emitted as well. The first SIGUSR2 will enable yappi, on the second
+SIGUSR2 it dumps the information collected, resets all yappi state and
+stops profiling. This is to minimize the impact of yappi on a running
+system.
diff --git a/requirements.txt b/requirements.txt
index 50726c0..dd947d6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@
Paste
WebOb>=1.2.3,<1.3
paramiko>=1.8.0
-GitPython>=0.3.2.RC1
+GitPython==0.3.2.RC1
lockfile>=0.8
ordereddict
python-daemon
diff --git a/test-requirements.txt b/test-requirements.txt
index 5192de7..c68b2db 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,9 +1,8 @@
hacking>=0.9.2,<0.10
coverage>=3.6
-sphinx>=1.1.2,<1.2
+sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
sphinxcontrib-blockdiag>=0.5.5
-docutils==0.9.1
discover
fixtures>=0.3.14
python-keystoneclient>=0.4.2
diff --git a/tests/fixtures/layouts/bad_misplaced_ref.yaml b/tests/fixtures/layouts/bad_misplaced_ref.yaml
new file mode 100644
index 0000000..f009c39
--- /dev/null
+++ b/tests/fixtures/layouts/bad_misplaced_ref.yaml
@@ -0,0 +1,13 @@
+pipelines:
+ - name: 'check'
+ manager: IndependentPipelineManager
+ trigger:
+ gerrit:
+ - event: patchset-created
+ ref: /some/ref/path
+
+projects:
+ - name: org/project
+ merge-mode: cherry-pick
+ check:
+ - project-check
diff --git a/zuul/layoutvalidator.py b/zuul/layoutvalidator.py
index 9a4e00f..6969653 100644
--- a/zuul/layoutvalidator.py
+++ b/zuul/layoutvalidator.py
@@ -18,6 +18,7 @@
import voluptuous as v
import string
+from zuul.trigger import gerrit
# Several forms accept either a single item or a list, this makes
# specifying that in the schema easy (and explicit).
@@ -271,3 +272,7 @@
if 'project-templates' in data:
self.checkDuplicateNames(
data['project-templates'], ['project-templates'])
+ for pipeline in data['pipelines']:
+ if 'gerrit' in pipeline['trigger']:
+ gerrit.validate_trigger(pipeline['trigger'])
+
diff --git a/zuul/lib/gerrit.py b/zuul/lib/gerrit.py
index 52e6057..5aad953 100644
--- a/zuul/lib/gerrit.py
+++ b/zuul/lib/gerrit.py
@@ -145,21 +145,34 @@
return data
def simpleQuery(self, query):
- args = '--current-patch-set'
- cmd = 'gerrit query --format json %s %s' % (
- args, query)
- out, err = self._ssh(cmd)
- if not out:
- return False
- lines = out.split('\n')
- if not lines:
- return False
- data = [json.loads(line) for line in lines[:-1]]
- if not data:
- return False
- self.log.debug("Received data from Gerrit query: \n%s" %
- (pprint.pformat(data)))
- return data
+ def _query_chunk(query):
+ args = '--current-patch-set'
+
+ cmd = 'gerrit query --format json %s %s' % (
+ args, query)
+ out, err = self._ssh(cmd)
+ if not out:
+ return False
+ lines = out.split('\n')
+ if not lines:
+ return False
+ data = [json.loads(line) for line in lines
+ if "sortKey" in line]
+ if not data:
+ return False
+ self.log.debug("Received data from Gerrit query: \n%s" %
+ (pprint.pformat(data)))
+ return data
+
+ # gerrit returns 500 results by default, so implement paging
+ # for large projects like nova
+ alldata = []
+ chunk = _query_chunk(query)
+ while(chunk):
+ alldata.extend(chunk)
+ sortkey = "resume_sortkey:'%s'" % chunk[-1]["sortKey"]
+ chunk = _query_chunk("%s %s" % (query, sortkey))
+ return alldata
def _open(self):
client = paramiko.SSHClient()
diff --git a/zuul/model.py b/zuul/model.py
index b03bbb0..67ce8be 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -709,6 +709,10 @@
ret['project'] = changeish.project.name
ret['enqueue_time'] = int(self.enqueue_time * 1000)
ret['jobs'] = []
+ if hasattr(changeish, 'owner'):
+ ret['owner'] = changeish.owner
+ else:
+ ret['owner'] = None
max_remaining = 0
for job in self.pipeline.getJobs(changeish):
now = time.time()
@@ -857,6 +861,7 @@
self.approvals = []
self.open = None
self.status = None
+ self.owner = None
def _id(self):
return '%s,%s' % (self.number, self.patchset)
diff --git a/zuul/trigger/gerrit.py b/zuul/trigger/gerrit.py
index 6966488..0c0a376 100644
--- a/zuul/trigger/gerrit.py
+++ b/zuul/trigger/gerrit.py
@@ -16,6 +16,7 @@
import threading
import time
import urllib2
+import voluptuous
from zuul.lib import gerrit
from zuul.model import TriggerEvent, Change, Ref, NullChange
@@ -330,7 +331,7 @@
self.log.debug("Running query %s to get project open changes" % (query,))
data = self.gerrit.simpleQuery(query)
changes = []
- for record in data[:-1]:
+ for record in data:
try:
changes.append(self._getChange(record['number'],
record['currentPatchSet']['number']))
@@ -371,6 +372,7 @@
change.approvals = data['currentPatchSet'].get('approvals', [])
change.open = data['open']
change.status = data['status']
+ change.owner = data['owner']
if change.is_merged:
# This change is merged, so we don't need to look any further
@@ -411,3 +413,14 @@
if sha:
url += ';a=commitdiff;h=' + sha
return url
+
+
+def validate_trigger(trigger_data):
+ """Validates the layout's trigger data."""
+ events_with_ref = ('ref-updated', )
+ for event in trigger_data['gerrit']:
+ if event['event'] not in events_with_ref and event.get('ref', False):
+ raise voluptuous.Invalid(
+ "The event %s does not include ref information, Zuul cannot "
+ "use ref filter 'ref: %s'" % (event['event'], event['ref']))
+