Controller driver base and pod project driver
This patch adds stevedore-based driver support to the controller and implements a driver to determine OpenStack project ID used to create pod ports. Change-Id: I0002ce1c1f7985955b7f66902dcf5386db212a1a Partially-Implements: blueprint kuryr-k8s-integration
This commit is contained in:
parent
f383c67b41
commit
363575fe4c
@ -33,12 +33,20 @@ k8s_opts = [
|
||||
cfg.StrOpt('api_root',
|
||||
help=_("The root URL of the Kubernetes API"),
|
||||
default=os.environ.get('K8S_API', 'http://localhost:8080')),
|
||||
cfg.StrOpt('pod_project_driver',
|
||||
help=_("The driver to determine OpenStack project for pod ports"),
|
||||
default='default'),
|
||||
]
|
||||
|
||||
neutron_defaults = [
|
||||
cfg.StrOpt('project',
|
||||
help=_("Default OpenStack project ID for Kubernetes resources")),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(kuryr_k8s_opts)
|
||||
CONF.register_opts(k8s_opts, group='kubernetes')
|
||||
CONF.register_opts(neutron_defaults, group='neutron_defaults')
|
||||
|
||||
CONF.register_opts(lib_config.core_opts)
|
||||
CONF.register_opts(lib_config.binding_opts, 'binding')
|
||||
|
0
kuryr_kubernetes/controller/drivers/__init__.py
Normal file
0
kuryr_kubernetes/controller/drivers/__init__.py
Normal file
95
kuryr_kubernetes/controller/drivers/base.py
Normal file
95
kuryr_kubernetes/controller/drivers/base.py
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2016 Mirantis, 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from kuryr.lib._i18n import _LE
|
||||
from oslo_log import log as logging
|
||||
from stevedore import driver as stv_driver
|
||||
|
||||
from kuryr_kubernetes import config
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_DRIVER_NAMESPACE_BASE = 'kuryr_kubernetes.controller.drivers'
|
||||
_DRIVER_MANAGERS = {}
|
||||
|
||||
|
||||
class DriverBase(object):
|
||||
"""Base class for controller drivers.
|
||||
|
||||
Subclasses must define an *ALIAS* attribute that is used to find a driver
|
||||
implementation by `get_instance` class method which utilises
|
||||
`stevedore.driver.DriverManager` with the namespace set to
|
||||
'kuryr_kubernetes.controller.drivers.*ALIAS*' and the name of
|
||||
the driver determined from the '[kubernetes]/*ALIAS*_driver' configuration
|
||||
parameter.
|
||||
|
||||
Usage example:
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class SomeDriverInterface(DriverBase):
|
||||
ALIAS = 'driver_alias'
|
||||
|
||||
@abc.abstractmethod
|
||||
def some_method(self):
|
||||
pass
|
||||
|
||||
driver = SomeDriverInterface.get_instance()
|
||||
driver.some_method()
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
"""Get an implementing driver instance."""
|
||||
|
||||
alias = cls.ALIAS
|
||||
|
||||
try:
|
||||
manager = _DRIVER_MANAGERS[alias]
|
||||
except KeyError:
|
||||
name = config.CONF.kubernetes[alias + '_driver']
|
||||
manager = stv_driver.DriverManager(
|
||||
namespace="%s.%s" % (_DRIVER_NAMESPACE_BASE, alias),
|
||||
name=name,
|
||||
invoke_on_load=True)
|
||||
_DRIVER_MANAGERS[alias] = manager
|
||||
|
||||
driver = manager.driver
|
||||
if not isinstance(driver, cls):
|
||||
raise TypeError(_LE("Invalid %(alias)r driver type: %(driver)s, "
|
||||
"must be a subclass of %(type)s") % {
|
||||
'alias': alias,
|
||||
'driver': driver.__class__.__name__,
|
||||
'type': cls})
|
||||
return driver
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class PodProjectDriver(DriverBase):
|
||||
"""Provides an OpenStack project ID for Kubernetes Pod ports."""
|
||||
|
||||
ALIAS = 'pod_project'
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_project(self, pod):
|
||||
"""Get an OpenStack project ID for Kubernetes Pod ports.
|
||||
|
||||
:param pod: dict containing Kubernetes Pod object
|
||||
:return: project ID
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
35
kuryr_kubernetes/controller/drivers/default_project.py
Normal file
35
kuryr_kubernetes/controller/drivers/default_project.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2016 Mirantis, 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 oslo_config import cfg
|
||||
|
||||
from kuryr_kubernetes import config
|
||||
from kuryr_kubernetes.controller.drivers import base
|
||||
|
||||
|
||||
class DefaultPodProjectDriver(base.PodProjectDriver):
|
||||
"""Provides project ID for Pod port based on a configuration option."""
|
||||
|
||||
def get_project(self, pod):
|
||||
project_id = config.CONF.neutron_defaults.project
|
||||
|
||||
if not project_id:
|
||||
# NOTE(ivc): this option is only required for
|
||||
# DefaultPodProjectDriver and its subclasses, but it may be
|
||||
# optional for other drivers (e.g. when each namespace has own
|
||||
# project)
|
||||
raise cfg.RequiredOptError('project', 'neutron_defaults')
|
||||
|
||||
return project_id
|
83
kuryr_kubernetes/tests/unit/controller/drivers/test_base.py
Normal file
83
kuryr_kubernetes/tests/unit/controller/drivers/test_base.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2016 Mirantis, 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.
|
||||
|
||||
import abc
|
||||
import mock
|
||||
import six
|
||||
|
||||
from kuryr_kubernetes.controller.drivers import base as d_base
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class _TestDriver(d_base.DriverBase):
|
||||
ALIAS = 'test_alias'
|
||||
|
||||
@abc.abstractmethod
|
||||
def test(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class TestDriverBase(test_base.TestCase):
|
||||
|
||||
@mock.patch.object(d_base, '_DRIVER_MANAGERS')
|
||||
@mock.patch('kuryr_kubernetes.config.CONF')
|
||||
@mock.patch('stevedore.driver.DriverManager')
|
||||
def test_get_instance(self, m_stv_mgr, m_cfg, m_mgrs):
|
||||
m_drv = mock.MagicMock(spec=_TestDriver)
|
||||
m_mgr = mock.MagicMock()
|
||||
m_mgr.driver = m_drv
|
||||
m_mgrs.__getitem__.return_value = m_mgr
|
||||
|
||||
self.assertEqual(m_drv, _TestDriver.get_instance())
|
||||
m_cfg.assert_not_called()
|
||||
m_stv_mgr.assert_not_called()
|
||||
|
||||
@mock.patch.object(d_base, '_DRIVER_MANAGERS')
|
||||
@mock.patch('kuryr_kubernetes.config.CONF')
|
||||
@mock.patch('stevedore.driver.DriverManager')
|
||||
def test_get_instance_not_loaded(self, m_stv_mgr, m_cfg, m_mgrs):
|
||||
alias = _TestDriver.ALIAS
|
||||
cfg_name = '%s_driver' % (alias)
|
||||
drv_name = 'driver_impl'
|
||||
namespace = '%s.%s' % (d_base._DRIVER_NAMESPACE_BASE, alias)
|
||||
m_cfg.kubernetes.__getitem__.return_value = drv_name
|
||||
m_drv = mock.MagicMock(spec=_TestDriver)
|
||||
m_mgr = mock.MagicMock()
|
||||
m_mgr.driver = m_drv
|
||||
m_stv_mgr.return_value = m_mgr
|
||||
m_mgrs.__getitem__.side_effect = KeyError
|
||||
|
||||
self.assertEqual(m_drv, _TestDriver.get_instance())
|
||||
m_cfg.kubernetes.__getitem__.assert_called_with(cfg_name)
|
||||
m_stv_mgr.assert_called_with(namespace=namespace, name=drv_name,
|
||||
invoke_on_load=True)
|
||||
m_mgrs.__setitem__.assert_called_once_with(alias, m_mgr)
|
||||
|
||||
@mock.patch.object(d_base, '_DRIVER_MANAGERS')
|
||||
@mock.patch('kuryr_kubernetes.config.CONF')
|
||||
@mock.patch('stevedore.driver.DriverManager')
|
||||
def test_get_instance_invalid_type(self, m_stv_mgr, m_cfg, m_mgrs):
|
||||
class _InvalidDriver(object):
|
||||
pass
|
||||
|
||||
m_drv = mock.MagicMock(spec=_InvalidDriver)
|
||||
m_mgr = mock.MagicMock()
|
||||
m_mgr.driver = m_drv
|
||||
m_mgrs.__getitem__.return_value = m_mgr
|
||||
|
||||
self.assertRaises(TypeError, _TestDriver.get_instance)
|
||||
m_cfg.assert_not_called()
|
||||
m_stv_mgr.assert_not_called()
|
@ -0,0 +1,39 @@
|
||||
# Copyright (c) 2016 Mirantis, 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.
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from kuryr_kubernetes.controller.drivers import default_project
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
|
||||
|
||||
class TestDefaultPodProjectDriver(test_base.TestCase):
|
||||
|
||||
@mock.patch('kuryr_kubernetes.config.CONF')
|
||||
def test_get_project(self, m_cfg):
|
||||
project_id = mock.sentinel.project_id
|
||||
pod = mock.sentinel.pod
|
||||
m_cfg.neutron_defaults.project = project_id
|
||||
driver = default_project.DefaultPodProjectDriver()
|
||||
|
||||
self.assertEqual(project_id, driver.get_project(pod))
|
||||
|
||||
def test_get_project_not_set(self):
|
||||
pod = mock.sentinel.pod
|
||||
driver = default_project.DefaultPodProjectDriver()
|
||||
|
||||
self.assertRaises(cfg.RequiredOptError, driver.get_project, pod)
|
@ -13,3 +13,4 @@ oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.service>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.16.0 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
stevedore>=1.17.1 # Apache-2.0
|
||||
|
@ -26,6 +26,9 @@ oslo.config.opts =
|
||||
console_scripts =
|
||||
kuryr-k8s-controller = kuryr_kubernetes.cmd.eventlet.controller:start
|
||||
|
||||
kuryr_kubernetes.controller.drivers.pod_project =
|
||||
default = kuryr_kubernetes.controller.drivers.default_project:DefaultPodProjectDriver
|
||||
|
||||
[files]
|
||||
packages =
|
||||
kuryr_kubernetes
|
||||
|
Loading…
Reference in New Issue
Block a user