Kuryr-Controller: make handlers pluggable
This patch introduces a new way for configuring which handlers the Kuryr controller should be using. This will allow people to use externally provided handlers as long as they are installed as entrypoints of the right namespace. Implements: blueprint kuryr-pluggable-handlers Change-Id: I52ce0ef00771c8587d7f7113cc5eb4839d1309a5 Signed-off-by: Antoni Segura Puimedon <antonisp@celebdor.com>
This commit is contained in:
parent
c8aa90029a
commit
d3a6cdcf99
@ -149,6 +149,10 @@ k8s_opts = [
|
||||
cfg.IntOpt('watch_retry_timeout',
|
||||
help=_('Time (in seconds) the watcher retries watching for.'),
|
||||
default=60),
|
||||
cfg.ListOpt('enabled_handlers',
|
||||
help=_("The comma-separated handlers that should be "
|
||||
"registered for watching in the pipeline."),
|
||||
default=['vif', 'lb', 'lbaasspec']),
|
||||
]
|
||||
|
||||
neutron_defaults = [
|
||||
|
@ -38,6 +38,7 @@ class LBaaSSpecHandler(k8s_base.ResourceEventHandler):
|
||||
"""
|
||||
|
||||
OBJECT_KIND = k_const.K8S_OBJ_SERVICE
|
||||
OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "services")
|
||||
|
||||
def __init__(self):
|
||||
super(LBaaSSpecHandler, self).__init__()
|
||||
@ -218,6 +219,7 @@ class LoadBalancerHandler(k8s_base.ResourceEventHandler):
|
||||
"""
|
||||
|
||||
OBJECT_KIND = k_const.K8S_OBJ_ENDPOINTS
|
||||
OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "endpoints")
|
||||
|
||||
def __init__(self):
|
||||
super(LoadBalancerHandler, self).__init__()
|
||||
|
@ -38,6 +38,7 @@ class VIFHandler(k8s_base.ResourceEventHandler):
|
||||
"""
|
||||
|
||||
OBJECT_KIND = constants.K8S_OBJ_POD
|
||||
OBJECT_WATCH_PATH = "%s/%s" % (constants.K8S_API_BASE, "pods")
|
||||
|
||||
def __init__(self):
|
||||
super(VIFHandler, self).__init__()
|
||||
|
@ -16,22 +16,52 @@
|
||||
import sys
|
||||
|
||||
import os_vif
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import service
|
||||
from stevedore.named import NamedExtensionManager
|
||||
|
||||
from kuryr_kubernetes import clients
|
||||
from kuryr_kubernetes import config
|
||||
from kuryr_kubernetes import constants
|
||||
from kuryr_kubernetes.controller.handlers import lbaas as h_lbaas
|
||||
from kuryr_kubernetes.controller.handlers import pipeline as h_pipeline
|
||||
from kuryr_kubernetes.controller.handlers import vif as h_vif
|
||||
from kuryr_kubernetes.controller.managers import health
|
||||
from kuryr_kubernetes import objects
|
||||
from kuryr_kubernetes import watcher
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _handler_not_found(names):
|
||||
LOG.exception('Handlers "%s" were not found.', names)
|
||||
LOG.critical('Handlers "%s" were not found.', names)
|
||||
raise SystemExit()
|
||||
|
||||
|
||||
def _handler_not_loaded(manager, entrypoint, exception):
|
||||
LOG.exception('Exception when loading handlers %s.', entrypoint)
|
||||
LOG.critical('Handlers entrypoint "%s" failed to load due to %s.',
|
||||
entrypoint, exception)
|
||||
raise SystemExit()
|
||||
|
||||
|
||||
def _load_kuryr_ctrlr_handlers():
|
||||
configured_handlers = CONF.kubernetes.enabled_handlers
|
||||
LOG.info('Configured handlers: %s', configured_handlers)
|
||||
handlers = NamedExtensionManager(
|
||||
'kuryr_kubernetes.controller.handlers',
|
||||
configured_handlers,
|
||||
invoke_on_load=True,
|
||||
on_missing_entrypoints_callback=_handler_not_found,
|
||||
on_load_failure_callback=_handler_not_loaded)
|
||||
LOG.info('Loaded handlers: %s', handlers.names())
|
||||
ctrlr_handlers = []
|
||||
for handler in handlers.extensions:
|
||||
ctrlr_handlers.append(handler.obj)
|
||||
return ctrlr_handlers
|
||||
|
||||
|
||||
class KuryrK8sService(service.Service):
|
||||
"""Kuryr-Kubernetes controller Service."""
|
||||
|
||||
@ -42,12 +72,11 @@ class KuryrK8sService(service.Service):
|
||||
pipeline = h_pipeline.ControllerPipeline(self.tg)
|
||||
self.watcher = watcher.Watcher(pipeline, self.tg)
|
||||
self.health_manager = health.HealthServer()
|
||||
# TODO(ivc): pluggable resource/handler registration
|
||||
for resource in ["pods", "services", "endpoints"]:
|
||||
self.watcher.add("%s/%s" % (constants.K8S_API_BASE, resource))
|
||||
pipeline.register(h_vif.VIFHandler())
|
||||
pipeline.register(h_lbaas.LBaaSSpecHandler())
|
||||
pipeline.register(h_lbaas.LoadBalancerHandler())
|
||||
|
||||
handlers = _load_kuryr_ctrlr_handlers()
|
||||
for handler in handlers:
|
||||
self.watcher.add(handler.get_watch_path())
|
||||
pipeline.register(handler)
|
||||
|
||||
def start(self):
|
||||
LOG.info("Service '%s' starting", self.__class__.__name__)
|
||||
|
@ -34,9 +34,13 @@ def object_uid(event):
|
||||
class ResourceEventHandler(dispatch.EventConsumer, health.HealthHandler):
|
||||
"""Base class for K8s event handlers.
|
||||
|
||||
Implementing classes should override the `OBJECT_KIND` attribute with a
|
||||
valid Kubernetes object type name (e.g. 'Pod' or 'Namespace'; see [1]
|
||||
for more details).
|
||||
Implementing classes should override both `OBJECT_KIND` and
|
||||
'OBJECT_WATCH_PATH' attributes.
|
||||
The `OBJECT_KIND` should be set to a valid Kubernetes object type
|
||||
name (e.g. 'Pod' or 'Namespace'; see [1] for more details).
|
||||
|
||||
The `OBJECT_WATCH_PATH` should point to object's watched path,
|
||||
(e.g. for the 'Pod' case the OBJECT_WATCH_PATH should be '/api/v1/pods').
|
||||
|
||||
Implementing classes are expected to override any or all of the
|
||||
`on_added`, `on_present`, `on_modified`, `on_deleted` methods that would
|
||||
@ -48,10 +52,14 @@ class ResourceEventHandler(dispatch.EventConsumer, health.HealthHandler):
|
||||
"""
|
||||
|
||||
OBJECT_KIND = None
|
||||
OBJECT_WATCH_PATH = None
|
||||
|
||||
def __init__(self):
|
||||
super(ResourceEventHandler, self).__init__()
|
||||
|
||||
def get_watch_path(self):
|
||||
return self.OBJECT_WATCH_PATH
|
||||
|
||||
@property
|
||||
def consumes(self):
|
||||
return {object_kind: self.OBJECT_KIND}
|
||||
|
@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2018 RedHat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 kuryr_kubernetes.handlers import k8s_base
|
||||
|
||||
|
||||
class TestHandler(k8s_base.ResourceEventHandler):
|
||||
|
||||
OBJECT_KIND = 'DUMMY'
|
||||
OBJECT_WATCH_PATH = 'DUMMY_PATH'
|
||||
|
||||
def __init__(self):
|
||||
super(TestHandler, self).__init__()
|
@ -17,6 +17,8 @@ import mock
|
||||
|
||||
from kuryr_kubernetes.controller import service
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
from kuryr_kubernetes.tests.unit.controller.handlers import test_fake_handler
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
class TestControllerService(test_base.TestCase):
|
||||
@ -39,3 +41,19 @@ class TestControllerService(test_base.TestCase):
|
||||
m_svc.assert_called()
|
||||
m_oslo_launch.assert_called()
|
||||
m_launcher.wait.assert_called()
|
||||
|
||||
def test_check_test_handler(self):
|
||||
cfg.CONF.set_override('enabled_handlers', ['test_handler'],
|
||||
group='kubernetes')
|
||||
handlers = service._load_kuryr_ctrlr_handlers()
|
||||
for handler in handlers:
|
||||
self.assertEqual(handler.get_watch_path(),
|
||||
test_fake_handler.TestHandler.OBJECT_WATCH_PATH)
|
||||
|
||||
@mock.patch('kuryr_kubernetes.controller.service._handler_not_found')
|
||||
def test_handler_not_found(self, m_handler_not_found):
|
||||
|
||||
cfg.CONF.set_override('enabled_handlers', ['fake_handler'],
|
||||
group='kubernetes')
|
||||
service._load_kuryr_ctrlr_handlers()
|
||||
m_handler_not_found.assert_called()
|
||||
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Introduced a pluggable interface for the Kuryr controller handlers.
|
||||
Each Controller handler associates itself with specific Kubernetes
|
||||
object kind and is expected to process the events of the watched
|
||||
Kubernetes API endpoints.
|
||||
The pluggable handlers framework enable both using externally provided
|
||||
handlers in Kuryr Controller and controlling which handlers
|
||||
should be active.
|
||||
|
||||
To control which Kuryr Controller handlers should be active, the selected
|
||||
handlers need to be included at the kuryr.conf at the 'kubernetes'
|
||||
section.
|
||||
If not specified, Kuryr Controller will run the default handlers.
|
||||
For example, to enable only the 'vif' controller handler we should set
|
||||
the following at kuryr.conf:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[kubernetes]
|
||||
enabled_handlers=vif
|
||||
|
@ -75,6 +75,12 @@ kuryr_kubernetes.controller.drivers.vif_pool =
|
||||
nested = kuryr_kubernetes.controller.drivers.vif_pool:NestedVIFPool
|
||||
multi_pool = kuryr_kubernetes.controller.drivers.vif_pool:MultiVIFPool
|
||||
|
||||
kuryr_kubernetes.controller.handlers =
|
||||
vif = kuryr_kubernetes.controller.handlers.vif:VIFHandler
|
||||
lbaasspec = kuryr_kubernetes.controller.handlers.lbaas:LBaaSSpecHandler
|
||||
lb = kuryr_kubernetes.controller.handlers.lbaas:LoadBalancerHandler
|
||||
test_handler = kuryr_kubernetes.tests.unit.controller.handlers.test_fake_handler:TestHandler
|
||||
|
||||
[files]
|
||||
packages =
|
||||
kuryr_kubernetes
|
||||
|
Loading…
Reference in New Issue
Block a user