commit
9672473d87
@ -1,46 +0,0 @@
|
||||
# Copyright 2014 Rackspace Australia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import testtools
|
||||
|
||||
import zuul.reporter
|
||||
|
||||
|
||||
class TestSMTPReporter(testtools.TestCase):
|
||||
log = logging.getLogger("zuul.test_reporter")
|
||||
|
||||
def setUp(self):
|
||||
super(TestSMTPReporter, self).setUp()
|
||||
|
||||
def test_reporter_abc(self):
|
||||
# We only need to instantiate a class for this
|
||||
reporter = zuul.reporter.smtp.SMTPReporter({}) # noqa
|
||||
|
||||
def test_reporter_name(self):
|
||||
self.assertEqual('smtp', zuul.reporter.smtp.SMTPReporter.name)
|
||||
|
||||
|
||||
class TestGerritReporter(testtools.TestCase):
|
||||
log = logging.getLogger("zuul.test_reporter")
|
||||
|
||||
def setUp(self):
|
||||
super(TestGerritReporter, self).setUp()
|
||||
|
||||
def test_reporter_abc(self):
|
||||
# We only need to instantiate a class for this
|
||||
reporter = zuul.reporter.gerrit.GerritReporter(None) # noqa
|
||||
|
||||
def test_reporter_name(self):
|
||||
self.assertEqual('gerrit', zuul.reporter.gerrit.GerritReporter.name)
|
@ -1,51 +0,0 @@
|
||||
# Copyright 2014 Rackspace Australia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import testtools
|
||||
|
||||
import zuul.trigger
|
||||
|
||||
|
||||
class TestGerritTrigger(testtools.TestCase):
|
||||
log = logging.getLogger("zuul.test_trigger")
|
||||
|
||||
def test_trigger_abc(self):
|
||||
# We only need to instantiate a class for this
|
||||
zuul.trigger.gerrit.GerritTrigger({})
|
||||
|
||||
def test_trigger_name(self):
|
||||
self.assertEqual('gerrit', zuul.trigger.gerrit.GerritTrigger.name)
|
||||
|
||||
|
||||
class TestTimerTrigger(testtools.TestCase):
|
||||
log = logging.getLogger("zuul.test_trigger")
|
||||
|
||||
def test_trigger_abc(self):
|
||||
# We only need to instantiate a class for this
|
||||
zuul.trigger.timer.TimerTrigger({})
|
||||
|
||||
def test_trigger_name(self):
|
||||
self.assertEqual('timer', zuul.trigger.timer.TimerTrigger.name)
|
||||
|
||||
|
||||
class TestZuulTrigger(testtools.TestCase):
|
||||
log = logging.getLogger("zuul.test_trigger")
|
||||
|
||||
def test_trigger_abc(self):
|
||||
# We only need to instantiate a class for this
|
||||
zuul.trigger.zuultrigger.ZuulTrigger({})
|
||||
|
||||
def test_trigger_name(self):
|
||||
self.assertEqual('zuul', zuul.trigger.zuultrigger.ZuulTrigger.name)
|
@ -0,0 +1,167 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class Driver(object):
|
||||
"""A Zuul Driver.
|
||||
|
||||
A Driver is an extension component of Zuul that supports
|
||||
interfacing with a remote system. It can support any of the
|
||||
following interfaces:
|
||||
|
||||
* Connection
|
||||
* Source
|
||||
* Trigger
|
||||
* Reporter
|
||||
|
||||
Drivers supporting each of these interfaces must implement some of
|
||||
the following methods, as appropriate.
|
||||
|
||||
Zuul will create a single instance of each Driver (which will be
|
||||
shared by all tenants), and this instance will persist for the
|
||||
life of the process. The Driver class may therefore manage any
|
||||
global state used by all connections.
|
||||
|
||||
The class or instance attribute **name** must be provided as a string.
|
||||
|
||||
"""
|
||||
|
||||
name = None
|
||||
|
||||
def getConnection(self, name, config):
|
||||
"""Create and return a new Connection object.
|
||||
|
||||
Required if this driver implements the Connection interface.
|
||||
|
||||
This method will be called once for each connection specified
|
||||
in zuul.conf. The resultant object should be responsible for
|
||||
establishing any long-lived connections to remote systems. If
|
||||
Zuul is reconfigured, all existing connections will be stopped
|
||||
and this method will be called again for any new connections
|
||||
which should be created.
|
||||
|
||||
When a connection is specified in zuul.conf with a name, that
|
||||
name is used here when creating the connection, and it is also
|
||||
used in the layout to attach triggers and reporters to the
|
||||
named connection. If the Driver does not utilize a connection
|
||||
(because it does not interact with a remote system), do not
|
||||
implement this method and Zuul will automatically associate
|
||||
triggers and reporters with the name of the Driver itself
|
||||
where it would normally expect the name of a connection.
|
||||
|
||||
:arg str name: The name of the connection. This is the name
|
||||
supplied in the zuul.conf file where the connection is
|
||||
configured.
|
||||
:arg dict config: The configuration information supplied along
|
||||
with the connection in zuul.conf.
|
||||
|
||||
:returns: A new Connection object.
|
||||
:rtype: Connection
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def getTrigger(self, connection, config=None):
|
||||
"""Create and return a new Connection object.
|
||||
|
||||
Required if this driver implements the Trigger interface.
|
||||
|
||||
:arg Connection connection: The Connection object associated
|
||||
with the trigger (as previously returned by getConnection)
|
||||
or None.
|
||||
:arg dict config: The configuration information supplied along
|
||||
with the trigger in the layout.
|
||||
|
||||
:returns: A new Trigger object.
|
||||
:rtype: Trigger
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def getSource(self, connection):
|
||||
"""Create and return a new Source object.
|
||||
|
||||
Required if this driver implements the Source interface.
|
||||
|
||||
:arg Connection connection: The Connection object associated
|
||||
with the source (as previously returned by getConnection).
|
||||
|
||||
:returns: A new Source object.
|
||||
:rtype: Source
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def getReporter(self, connection, config=None):
|
||||
"""Create and return a new Reporter object.
|
||||
|
||||
Required if this driver implements the Reporter interface.
|
||||
|
||||
:arg Connection connection: The Connection object associated
|
||||
with the reporter (as previously returned by getConnection)
|
||||
or None.
|
||||
:arg dict config: The configuration information supplied along
|
||||
with the reporter in the layout.
|
||||
|
||||
:returns: A new Reporter object.
|
||||
:rtype: Reporter
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def getTriggerSchema(self):
|
||||
"""Get the schema for this driver's trigger.
|
||||
|
||||
Required if this driver implements the Trigger interface.
|
||||
|
||||
:returns: A voluptuous schema.
|
||||
:rtype: dict or Schema
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def getReporterSchema(self):
|
||||
"""Get the schema for this driver's reporter.
|
||||
|
||||
Required if this driver implements the Reporter interface.
|
||||
|
||||
:returns: A voluptuous schema.
|
||||
:rtype: dict or Schema
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def reconfigure(self, tenant):
|
||||
"""Called when a tenant is reconfigured.
|
||||
|
||||
When Zuul performs a reconfiguration for a tenant, this method
|
||||
is called with the tenant (including the new layout
|
||||
configuration) as an argument. The driver may establish any
|
||||
global resources needed by the tenant at this point.
|
||||
|
||||
:arg Tenant tenant: The tenant which has been reconfigured.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def registerScheduler(self, scheduler):
|
||||
"""Register the scheduler with the driver.
|
||||
|
||||
This method is called once during initialization to allow the
|
||||
driver to store a handle to the running scheduler.
|
||||
|
||||
:arg Scheduler scheduler: The current running scheduler.
|
||||
|
||||
"""
|
||||
pass
|
@ -0,0 +1,40 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import gerritconnection
|
||||
import gerrittrigger
|
||||
import gerritsource
|
||||
import gerritreporter
|
||||
|
||||
|
||||
class GerritDriver(object):
|
||||
name = 'gerrit'
|
||||
|
||||
def getConnection(self, name, config):
|
||||
return gerritconnection.GerritConnection(self, name, config)
|
||||
|
||||
def getTrigger(self, connection, config=None):
|
||||
return gerrittrigger.GerritTrigger(self, connection, config)
|
||||
|
||||
def getSource(self, connection):
|
||||
return gerritsource.GerritSource(self, connection)
|
||||
|
||||
def getReporter(self, connection, config=None):
|
||||
return gerritreporter.GerritReporter(self, connection, config)
|
||||
|
||||
def getTriggerSchema(self):
|
||||
return gerrittrigger.getSchema()
|
||||
|
||||
def getReporterSchema(self):
|
||||
return gerritreporter.getSchema()
|
@ -0,0 +1,94 @@
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
from zuul.model import TriggerEvent
|
||||
import timertrigger
|
||||
|
||||
|
||||
class TimerDriver(object):
|
||||
name = 'timer'
|
||||
|
||||
log = logging.getLogger("zuul.Timer")
|
||||
|
||||
def __init__(self):
|
||||
self.apsched = BackgroundScheduler()
|
||||
self.apsched.start()
|
||||
self.tenant_jobs = {}
|
||||
|
||||
def registerScheduler(self, scheduler):
|
||||
self.sched = scheduler
|
||||
|
||||
def reconfigure(self, tenant):
|
||||
self._removeJobs(tenant)
|
||||
self._addJobs(tenant)
|
||||
|
||||
def _removeJobs(self, tenant):
|
||||
jobs = self.tenant_jobs.get(tenant.name, [])
|
||||
for job in jobs:
|
||||
job.remove()
|
||||
|
||||
def _addJobs(self, tenant):
|
||||
jobs = []
|
||||
self.tenant_jobs[tenant.name] = jobs
|
||||
for pipeline in tenant.layout.pipelines:
|
||||
for ef in pipeline.manager.event_filters:
|
||||
if not isinstance(ef.trigger, timertrigger.TimerTrigger):
|
||||
continue
|
||||
for timespec in ef.timespecs:
|
||||
parts = timespec.split()
|
||||
if len(parts) < 5 or len(parts) > 6:
|
||||
self.log.error(
|
||||
"Unable to parse time value '%s' "
|
||||
"defined in pipeline %s" % (
|
||||
timespec,
|
||||
pipeline.name))
|
||||
continue
|
||||
minute, hour, dom, month, dow = parts[:5]
|
||||
if len(parts) > 5:
|
||||
second = parts[5]
|
||||
else:
|
||||
second = None
|
||||
trigger = CronTrigger(day=dom, day_of_week=dow, hour=hour,
|
||||
minute=minute, second=second)
|
||||
|
||||
job = self.apsched.add_job(
|
||||
self._onTrigger, trigger=trigger,
|
||||
args=(tenant, pipeline.name, timespec,))
|
||||
jobs.append(job)
|
||||
|
||||
def _onTrigger(self, tenant, pipeline_name, timespec):
|
||||
for project in tenant.layout.projects.values():
|
||||
event = TriggerEvent()
|
||||
event.type = 'timer'
|
||||
event.timespec = timespec
|
||||
event.forced_pipeline = pipeline_name
|
||||
event.project_name = project.name
|
||||
self.log.debug("Adding event %s" % event)
|
||||
self.sched.addEvent(event)
|
||||
|
||||
def stop(self):
|
||||
self.apsched.shutdown()
|
||||
|
||||
def getTrigger(self, connection_name):
|
||||
return timertrigger.TimerTrigger(self)
|
||||
|
||||
def getTriggerSchema(self):
|
||||
return timertrigger.getSchema()
|
@ -0,0 +1,46 @@
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import voluptuous as v
|
||||
|
||||
from zuul.model import EventFilter
|
||||
from zuul.trigger import BaseTrigger
|
||||
|
||||
|
||||
class TimerTrigger(BaseTrigger):
|
||||
name = 'timer'
|
||||
|
||||
def getEventFilters(self, trigger_conf):
|
||||
def toList(item):
|
||||
if not item:
|
||||
return []
|
||||
if isinstance(item, list):
|
||||
return item
|
||||
return [item]
|
||||
|
||||
efilters = []
|
||||
for trigger in toList(trigger_conf):
|
||||
f = EventFilter(trigger=self,
|
||||
types=['timer'],
|
||||
timespecs=toList(trigger['time']))
|
||||
|
||||
efilters.append(f)
|
||||
|
||||
return efilters
|
||||
|
||||
|
||||
def getSchema():
|
||||
timer_trigger = {v.Required('time'): str}
|
||||
return timer_trigger
|
@ -0,0 +1,111 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from zuul.model import TriggerEvent
|
||||
|
||||
import zuultrigger
|
||||
|
||||
PARENT_CHANGE_ENQUEUED = 'parent-change-enqueued'
|
||||
PROJECT_CHANGE_MERGED = 'project-change-merged'
|
||||
|
||||
|
||||
class ZuulDriver(object):
|
||||
name = 'zuul'
|
||||
log = logging.getLogger("zuul.ZuulTrigger")
|
||||
|
||||
def __init__(self):
|
||||
self.tenant_events = {}
|
||||
|
||||
def registerScheduler(self, scheduler):
|
||||
self.sched = scheduler
|
||||
|
||||
def reconfigure(self, tenant):
|
||||
events = set()
|
||||
self.tenant_events[tenant.name] = events
|
||||
for pipeline in tenant.layout.pipelines.values():
|
||||
for ef in pipeline.manager.event_filters:
|
||||
if not isinstance(ef.trigger, zuultrigger.ZuulTrigger):
|
||||
continue
|
||||
if PARENT_CHANGE_ENQUEUED in ef._types:
|
||||
events.add(PARENT_CHANGE_ENQUEUED)
|
||||
elif PROJECT_CHANGE_MERGED in ef._types:
|
||||
events.add(PROJECT_CHANGE_MERGED)
|
||||
|
||||
def onChangeMerged(self, tenant, change, source):
|
||||
# Called each time zuul merges a change
|
||||
if PROJECT_CHANGE_MERGED in self.tenant_events[tenant.name]:
|
||||
try:
|
||||
self._createProjectChangeMergedEvents(change, source)
|
||||
except Exception:
|
||||
self.log.exception(
|
||||
"Unable to create project-change-merged events for "
|
||||
"%s" % (change,))
|
||||
|
||||
def onChangeEnqueued(self, tenant, change, pipeline):
|
||||
self.log.debug("onChangeEnqueued %s", self.tenant_events[tenant.name])
|
||||
# Called each time a change is enqueued in a pipeline
|
||||
if PARENT_CHANGE_ENQUEUED in self.tenant_events[tenant.name]:
|
||||
try:
|
||||
self._createParentChangeEnqueuedEvents(change, pipeline)
|
||||
except Exception:
|
||||
self.log.exception(
|
||||
"Unable to create parent-change-enqueued events for "
|
||||
"%s in %s" % (change, pipeline))
|
||||
|
||||
def _createProjectChangeMergedEvents(self, change, source):
|
||||
changes = source.getProjectOpenChanges(
|
||||
change.project)
|
||||
for open_change in changes:
|
||||
self._createProjectChangeMergedEvent(open_change)
|
||||
|
||||
def _createProjectChangeMergedEvent(self, change):
|
||||
event = TriggerEvent()
|
||||
event.type = PROJECT_CHANGE_MERGED
|
||||
event.trigger_name = self.name
|
||||
event.project_name = change.project.name
|
||||
event.change_number = change.number
|
||||
event.branch = change.branch
|
||||
event.change_url = change.url
|
||||
event.patch_number = change.patchset
|
||||
event.refspec = change.refspec
|
||||
self.sched.addEvent(event)
|
||||
|
||||
def _createParentChangeEnqueuedEvents(self, change, pipeline):
|
||||
self.log.debug("Checking for changes needing %s:" % change)
|
||||
if not hasattr(change, 'needed_by_changes'):
|
||||
self.log.debug(" Changeish does not support dependencies")
|
||||
return
|
||||
for needs in change.needed_by_changes:
|
||||
self._createParentChangeEnqueuedEvent(needs, pipeline)
|
||||
|
||||
def _createParentChangeEnqueuedEvent(self, change, pipeline):
|
||||
event = TriggerEvent()
|
||||
event.type = PARENT_CHANGE_ENQUEUED
|
||||
event.trigger_name = self.name
|
||||
event.pipeline_name = pipeline.name
|
||||
event.project_name = change.project.name
|
||||
event.change_number = change.number
|
||||
event.branch = change.branch
|
||||
event.change_url = change.url
|
||||
event.patch_number = change.patchset
|
||||
event.refspec = change.refspec
|
||||
self.sched.addEvent(event)
|
||||
|
||||
def getTrigger(self, connection_name, config=None):
|
||||
return zuultrigger.ZuulTrigger(self, config)
|
||||
|
||||
def getTriggerSchema(self):
|
||||
return zuultrigger.getSchema()
|
@ -0,0 +1,77 @@
|
||||
# Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import voluptuous as v
|
||||
from zuul.model import EventFilter
|
||||
from zuul.trigger import BaseTrigger
|
||||
|
||||
|
||||
class ZuulTrigger(BaseTrigger):
|
||||
name = 'zuul'
|
||||
log = logging.getLogger("zuul.ZuulTrigger")
|
||||
|
||||
def __init__(self, connection, config=None):
|
||||
super(ZuulTrigger, self).__init__(connection, config)
|
||||
self._handle_parent_change_enqueued_events = False
|
||||
self._handle_project_change_merged_events = False
|
||||
|
||||
def getEventFilters(self, trigger_conf):
|
||||
def toList(item):
|
||||
if not item:
|
||||
return []
|
||||
if isinstance(item, list):
|
||||
return item
|
||||
return [item]
|
||||
|
||||
efilters = []
|
||||
for trigger in toList(trigger_conf):
|
||||
f = EventFilter(
|
||||
trigger=self,
|
||||
types=toList(trigger['event']),
|
||||
pipelines=toList(trigger.get('pipeline')),
|
||||
required_approvals=(
|
||||
toList(trigger.get('require-approval'))
|
||||
),
|
||||
reject_approvals=toList(
|
||||
trigger.get('reject-approval')
|
||||
),
|
||||
)
|
||||
efilters.append(f)
|
||||
|
||||
return efilters
|
||||
|
||||
|
||||
def getSchema():
|
||||
def toList(x):
|
||||
return v.Any([x], x)
|
||||
|
||||
approval = v.Schema({'username': str,
|
||||
'email-filter': str,
|
||||
'email': str,
|
||||
'older-than': str,
|
||||
'newer-than': str,
|
||||
}, extra=True)
|
||||
|
||||
zuul_trigger = {
|
||||
v.Required('event'):
|
||||
toList(v.Any('parent-change-enqueued',
|
||||
'project-change-merged')),
|
||||
'pipeline': toList(str),
|
||||
'require-approval': toList(approval),
|
||||
'reject-approval': toList(approval),
|
||||
}
|
||||
|
||||
return zuul_trigger
|
@ -1,93 +0,0 @@
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
import logging
|
||||
import voluptuous as v
|
||||
from zuul.model import EventFilter, TriggerEvent
|
||||
from zuul.trigger import BaseTrigger
|
||||
|
||||
|
||||
class TimerTrigger(BaseTrigger):
|
||||
name = 'timer'
|
||||
log = logging.getLogger("zuul.Timer")
|
||||
|
||||
def __init__(self, trigger_config={}, connection=None):
|
||||
super(TimerTrigger, self).__init__(trigger_config, connection)
|
||||
self.apsched = BackgroundScheduler()
|
||||
self.apsched.start()
|
||||
|
||||
def _onTrigger(self, pipeline_name, timespec):
|
||||
for project in self.sched.layout.projects.values():
|
||||
event = TriggerEvent()
|
||||
event.type = 'timer'
|
||||
event.timespec = timespec
|
||||
event.forced_pipeline = pipeline_name
|
||||
event.project_name = project.name
|
||||
self.log.debug("Adding event %s" % event)
|
||||
self.connection.sched.addEvent(event)
|
||||
|
||||
def stop(self):
|
||||
self.apsched.shutdown()
|
||||
|
||||
def getEventFilters(self, trigger_conf):
|
||||
def toList(item):
|
||||
if not item:
|
||||
return []
|
||||
if isinstance(item, list):
|
||||
return item
|
||||
return [item]
|
||||
|
||||
efilters = []
|
||||
for trigger in toList(trigger_conf):
|
||||
f = EventFilter(trigger=self,
|
||||
types=['timer'],
|
||||
timespecs=toList(trigger['time']))
|
||||
|
||||
efilters.append(f)
|
||||
|
||||
return efilters
|
||||
|
||||
def postConfig(self, pipeline):
|
||||
for job in self.apsched.get_jobs():
|
||||
job.remove()
|
||||
for ef in pipeline.manager.event_filters:
|
||||
if ef.trigger != self:
|
||||
continue
|
||||
for timespec in ef.timespecs:
|
||||
parts = timespec.split()
|
||||
if len(parts) < 5 or len(parts) > 6:
|
||||
self.log.error(
|
||||
"Unable to parse time value '%s' "
|
||||
"defined in pipeline %s" % (
|
||||
timespec,
|
||||
pipeline.name))
|
||||
continue
|
||||
minute, hour, dom, month, dow = parts[:5]
|
||||
if len(parts) > 5:
|
||||
second = parts[5]
|
||||
else:
|
||||
second = None
|
||||
trigger = CronTrigger(day=dom, day_of_week=dow, hour=hour,
|
||||
minute=minute, second=second)
|
||||
|
||||
self.apsched.add_job(self._onTrigger, trigger=trigger,
|
||||
args=(pipeline.name, timespec,))
|
||||
|
||||
|
||||
def getSchema():
|
||||
timer_trigger = {v.Required('time'): str}
|
||||
return timer_trigger
|
@ -1,147 +0,0 @@
|
||||
# Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||