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',
|
cfg.StrOpt('api_root',
|
||||||
help=_("The root URL of the Kubernetes API"),
|
help=_("The root URL of the Kubernetes API"),
|
||||||
default=os.environ.get('K8S_API', 'http://localhost:8080')),
|
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 = cfg.CONF
|
||||||
CONF.register_opts(kuryr_k8s_opts)
|
CONF.register_opts(kuryr_k8s_opts)
|
||||||
CONF.register_opts(k8s_opts, group='kubernetes')
|
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.core_opts)
|
||||||
CONF.register_opts(lib_config.binding_opts, 'binding')
|
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.service>=1.10.0 # Apache-2.0
|
||||||
oslo.utils>=3.16.0 # Apache-2.0
|
oslo.utils>=3.16.0 # Apache-2.0
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
|
stevedore>=1.17.1 # Apache-2.0
|
||||||
|
@ -26,6 +26,9 @@ oslo.config.opts =
|
|||||||
console_scripts =
|
console_scripts =
|
||||||
kuryr-k8s-controller = kuryr_kubernetes.cmd.eventlet.controller:start
|
kuryr-k8s-controller = kuryr_kubernetes.cmd.eventlet.controller:start
|
||||||
|
|
||||||
|
kuryr_kubernetes.controller.drivers.pod_project =
|
||||||
|
default = kuryr_kubernetes.controller.drivers.default_project:DefaultPodProjectDriver
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
kuryr_kubernetes
|
kuryr_kubernetes
|
||||||
|
Loading…
Reference in New Issue
Block a user