Move conductor common implementations into module

This allows common implementations to be reused later. Below is
the list of methods that were moved and renamed:
* handlers.k8s_conductor._retrieve_bay -> utils.retrieve_bay
* handlers.bay_conductor._get_baymodel -> utils.retrieve_bay
* handlers.k8s_conductor._retrieve_baymodel -> utils.retrieve_baymodel
* handlers.k8s_conductor._object_has_stack -> utils.object_has_stack
* handlers.k8s_conductor._retrieve_k8s_master_url ->
                                      k8s_api._retrieve_k8s_master_url
* handlers.k8s_conductor.Handler.k8s_api -> k8s_api.create_k8s_api

Change-Id: Ia10625f35667fd9309dc3627e2f2d31455dd19be
Partially-Implements: blueprint magnum-smart-bay-scale-down
This commit is contained in:
Hongbin Lu 2015-06-27 17:48:09 -04:00
parent c80e24203f
commit 8d9649bc1f
10 changed files with 320 additions and 349 deletions

View File

@ -22,6 +22,7 @@ from magnum.common import clients
from magnum.common import exception
from magnum.common import short_id
from magnum.conductor.template_definition import TemplateDefinition as TDef
from magnum.conductor import utils as conductor_utils
from magnum.i18n import _
from magnum.i18n import _LE
from magnum.i18n import _LI
@ -54,13 +55,8 @@ cfg.CONF.register_opts(bay_heat_opts, group='bay_heat')
LOG = logging.getLogger(__name__)
def _get_baymodel(context, bay):
baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id)
return baymodel
def _extract_template_definition(context, bay):
baymodel = _get_baymodel(context, bay)
baymodel = conductor_utils.retrieve_baymodel(context, bay)
cluster_distro = baymodel.cluster_distro
cluster_coe = baymodel.coe
definition = TDef.get_template_definition('vm', cluster_distro,
@ -108,7 +104,7 @@ def _update_stack(context, osc, bay):
def _update_stack_outputs(context, stack, bay):
baymodel = _get_baymodel(context, bay)
baymodel = conductor_utils.retrieve_baymodel(context, bay)
cluster_distro = baymodel.cluster_distro
cluster_coe = baymodel.coe
definition = TDef.get_template_definition('vm', cluster_distro,

View File

@ -21,6 +21,7 @@ from magnum.common import docker_utils
from magnum.common import exception
from magnum.common import utils
from magnum.conductor.handlers.common import docker_client
from magnum.conductor import utils as conductor_utils
from magnum.i18n import _LE
from magnum import objects
from magnum.objects import container as obj_container
@ -100,7 +101,7 @@ class Handler(object):
@classmethod
def _docker_for_container(cls, context, container):
bay = objects.Bay.get_by_uuid(context, container.bay_uuid)
bay = conductor_utils.retrieve_bay(context, container)
return cls._docker_for_bay(bay)
@classmethod

View File

@ -12,15 +12,12 @@
"""Magnum Kubernetes RPC handler."""
from oslo_config import cfg
from oslo_log import log as logging
from magnum.common import clients
from magnum.common import exception
from magnum.common import k8s_manifest
from magnum.common.pythonk8sclient.client import ApivbetaApi
from magnum.common.pythonk8sclient.client import swagger
from magnum.i18n import _
from magnum.conductor import k8s_api as k8s
from magnum.conductor import utils as conductor_utils
from magnum import objects
import ast
@ -29,58 +26,6 @@ from six.moves.urllib import error
LOG = logging.getLogger(__name__)
kubernetes_opts = [
cfg.StrOpt('k8s_protocol',
default='http',
help=_('Default protocol of k8s master endpoint '
'(http or https).')),
cfg.IntOpt('k8s_port',
default=8080,
help=_('Default port of the k8s master endpoint.')),
]
cfg.CONF.register_opts(kubernetes_opts, group='kubernetes')
def _retrieve_bay(context, obj):
bay_uuid = obj.bay_uuid
return objects.Bay.get_by_uuid(context, bay_uuid)
def _retrieve_baymodel(context, obj):
return objects.BayModel.get_by_uuid(context, obj.baymodel_id)
def _retrieve_k8s_master_url(context, obj):
apiserver_port = cfg.CONF.kubernetes.k8s_port
if hasattr(obj, 'bay_uuid'):
obj = _retrieve_bay(context, obj)
baymodel = _retrieve_baymodel(context, obj)
if baymodel.apiserver_port is not None:
apiserver_port = baymodel.apiserver_port
params = {
'k8s_protocol': cfg.CONF.kubernetes.k8s_protocol,
'k8s_port': apiserver_port,
'api_address': obj.api_address
}
return "%(k8s_protocol)s://%(api_address)s:%(k8s_port)s" % params
def _object_has_stack(context, obj):
osc = clients.OpenStackClients(context)
if hasattr(obj, 'bay_uuid'):
obj = _retrieve_bay(context, obj)
stack = osc.heat().stacks.get(obj.stack_id)
if (stack.stack_status == 'DELETE_COMPLETE' or
stack.stack_status == 'DELETE_IN_PROGRESS'):
return False
else:
return True
class Handler(object):
"""These are the backend operations. They are executed by the backend
service. API calls via AMQP (within the ReST API) trigger the
@ -90,30 +35,10 @@ class Handler(object):
def __init__(self):
super(Handler, self).__init__()
self._k8s_api = None
@property
def k8s_api(self):
return self._k8s_api
@k8s_api.setter
def k8s_api(self, k8s_master_url):
"""Creates connection with Kubernetes master and
creates ApivbetaApi instance to call Kubernetes
APIs.
:param k8s_master_url: Kubernetes master URL
"""
# build a connection with Kubernetes master
client = swagger.ApiClient(k8s_master_url)
# create the ApivbetaApi class instance
self._k8s_api = ApivbetaApi.ApivbetaApi(client)
def service_create(self, context, service):
LOG.debug("service_create")
k8s_master_url = _retrieve_k8s_master_url(context, service)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, service)
manifest = k8s_manifest.parse(service.manifest)
try:
self.k8s_api.createService(body=manifest,
@ -127,8 +52,7 @@ class Handler(object):
def service_update(self, context, service):
LOG.debug("service_update %s", service.uuid)
k8s_master_url = _retrieve_k8s_master_url(context, service)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, service)
manifest = k8s_manifest.parse(service.manifest)
try:
self.k8s_api.replaceService(name=service.name,
@ -145,9 +69,8 @@ class Handler(object):
def service_delete(self, context, uuid):
LOG.debug("service_delete %s", uuid)
service = objects.Service.get_by_uuid(context, uuid)
k8s_master_url = _retrieve_k8s_master_url(context, service)
self.k8s_api = k8s_master_url
if _object_has_stack(context, service):
self.k8s_api = k8s.create_k8s_api(context, service)
if conductor_utils.object_has_stack(context, service):
try:
self.k8s_api.deleteService(name=service.name,
namespaces='default')
@ -164,8 +87,7 @@ class Handler(object):
# Pod Operations
def pod_create(self, context, pod):
LOG.debug("pod_create")
k8s_master_url = _retrieve_k8s_master_url(context, pod)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, pod)
manifest = k8s_manifest.parse(pod.manifest)
try:
resp = self.k8s_api.createPod(body=manifest, namespaces='default')
@ -187,8 +109,7 @@ class Handler(object):
def pod_update(self, context, pod):
LOG.debug("pod_update %s", pod.uuid)
k8s_master_url = _retrieve_k8s_master_url(context, pod)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, pod)
manifest = k8s_manifest.parse(pod.manifest)
try:
self.k8s_api.replacePod(name=pod.name, body=manifest,
@ -204,9 +125,8 @@ class Handler(object):
def pod_delete(self, context, uuid):
LOG.debug("pod_delete %s", uuid)
pod = objects.Pod.get_by_uuid(context, uuid)
k8s_master_url = _retrieve_k8s_master_url(context, pod)
self.k8s_api = k8s_master_url
if _object_has_stack(context, pod):
self.k8s_api = k8s.create_k8s_api(context, pod)
if conductor_utils.object_has_stack(context, pod):
try:
self.k8s_api.deletePod(name=pod.name,
namespaces='default')
@ -223,8 +143,7 @@ class Handler(object):
# Replication Controller Operations
def rc_create(self, context, rc):
LOG.debug("rc_create")
k8s_master_url = _retrieve_k8s_master_url(context, rc)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, rc)
manifest = k8s_manifest.parse(rc.manifest)
try:
self.k8s_api.createReplicationController(body=manifest,
@ -238,8 +157,7 @@ class Handler(object):
def rc_update(self, context, rc):
LOG.debug("rc_update %s", rc.uuid)
k8s_master_url = _retrieve_k8s_master_url(context, rc)
self.k8s_api = k8s_master_url
self.k8s_api = k8s.create_k8s_api(context, rc)
manifest = k8s_manifest.parse(rc.manifest)
try:
self.k8s_api.replaceReplicationController(name=rc.name,
@ -256,9 +174,8 @@ class Handler(object):
def rc_delete(self, context, uuid):
LOG.debug("rc_delete %s", uuid)
rc = objects.ReplicationController.get_by_uuid(context, uuid)
k8s_master_url = _retrieve_k8s_master_url(context, rc)
self.k8s_api = k8s_master_url
if _object_has_stack(context, rc):
self.k8s_api = k8s.create_k8s_api(context, rc)
if conductor_utils.object_has_stack(context, rc):
try:
self.k8s_api.deleteReplicationController(name=rc.name,
namespaces='default')

View File

@ -0,0 +1,74 @@
# Copyright 2015 Huawei Technologies Co.,LTD.
#
# 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 magnum.common.pythonk8sclient.client import ApivbetaApi
from magnum.common.pythonk8sclient.client import swagger
from magnum.conductor import utils
from magnum.i18n import _
kubernetes_opts = [
cfg.StrOpt('k8s_protocol',
default='http',
help=_('Default protocol of k8s master endpoint '
'(http or https).')),
cfg.IntOpt('k8s_port',
default=8080,
help=_('Default port of the k8s master endpoint.')),
]
cfg.CONF.register_opts(kubernetes_opts, group='kubernetes')
class K8sAPI(ApivbetaApi.ApivbetaApi):
def __init__(self, context, obj):
# retrieve the URL of the k8s API endpoint
k8s_master_url = self._retrieve_k8s_master_url(context, obj)
# build a connection with Kubernetes master
client = swagger.ApiClient(k8s_master_url)
super(K8sAPI, self).__init__(client)
@staticmethod
def _retrieve_k8s_master_url(context, obj):
apiserver_port = cfg.CONF.kubernetes.k8s_port
if hasattr(obj, 'bay_uuid'):
obj = utils.retrieve_bay(context, obj)
baymodel = utils.retrieve_baymodel(context, obj)
if baymodel.apiserver_port is not None:
apiserver_port = baymodel.apiserver_port
params = {
'k8s_protocol': cfg.CONF.kubernetes.k8s_protocol,
'k8s_port': apiserver_port,
'api_address': obj.api_address
}
return "%(k8s_protocol)s://%(api_address)s:%(k8s_port)s" % params
def create_k8s_api(context, obj):
"""Create a kubernetes API client
Creates connection with Kubernetes master and creates ApivbetaApi instance
to call Kubernetes APIs.
:param context: The security context
:param obj: A bay or a k8s object (Pod, Service, ReplicationController)
"""
return K8sAPI(context, obj)

37
magnum/conductor/utils.py Normal file
View File

@ -0,0 +1,37 @@
# Copyright 2015 Huawei Technologies Co.,LTD.
#
# 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 magnum.common import clients
from magnum import objects
def retrieve_bay(context, obj):
return objects.Bay.get_by_uuid(context, obj.bay_uuid)
def retrieve_baymodel(context, bay):
return objects.BayModel.get_by_uuid(context, bay.baymodel_id)
def object_has_stack(context, obj):
osc = clients.OpenStackClients(context)
if hasattr(obj, 'bay_uuid'):
obj = retrieve_bay(context, obj)
stack = osc.heat().stacks.get(obj.stack_id)
if (stack.stack_status == 'DELETE_COMPLETE' or
stack.stack_status == 'DELETE_IN_PROGRESS'):
return False
return True

View File

@ -44,5 +44,5 @@ def list_opts():
('heat_client', magnum.common.clients.heat_client_opts),
('bay_heat', magnum.conductor.handlers.bay_conductor.bay_heat_opts),
('kubernetes',
magnum.conductor.handlers.k8s_conductor.kubernetes_opts),
magnum.conductor.k8s_api.kubernetes_opts),
]

View File

@ -54,15 +54,6 @@ class TestBayConductorWithK8s(base.TestCase):
'node_count': 1,
}
@patch('magnum.objects.BayModel.get_by_uuid')
def test_get_baymodel(self, mock_objects_baymodel_get_by_uuid):
baymodel = objects.BayModel(self.context, **self.baymodel_dict)
mock_objects_baymodel_get_by_uuid.return_value = baymodel
bay = objects.Bay(self.context, **self.bay_dict)
fetched_baymodel = bay_conductor._get_baymodel(self.context, bay)
self.assertEqual(baymodel, fetched_baymodel)
@patch('magnum.objects.BayModel.get_by_uuid')
def test_extract_template_definition(
self,

View File

@ -50,89 +50,12 @@ class TestK8sConductor(base.TestCase):
def mock_baymodel(self):
return objects.BayModel({})
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_pod(self,
mock_bay_get_by_uuid):
expected_context = 'context'
expected_bay_uuid = 'bay_uuid'
pod = self.mock_pod()
pod.bay_uuid = expected_bay_uuid
k8s_conductor._retrieve_bay(expected_context, pod)
mock_bay_get_by_uuid.assert_called_once_with(expected_context,
expected_bay_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
@patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_k8s_master_url_from_pod(
self,
mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid):
expected_context = 'context'
expected_api_address = 'api_address'
expected_baymodel_id = 'e74c40e0-d825-11e2-a28f-0800200c9a61'
expected_apiserver_port = 9999
pod = self.mock_pod()
pod.bay_uuid = 'bay_uuid'
bay = self.mock_bay()
bay.api_address = expected_api_address
bay.baymodel_id = expected_baymodel_id
baymodel = self.mock_baymodel()
baymodel.apiserver_port = expected_apiserver_port
mock_bay_get_by_uuid.return_value = bay
mock_baymodel_get_by_uuid.return_value = baymodel
actual_api_address = k8s_conductor._retrieve_k8s_master_url(
expected_context, pod)
self.assertEqual("http://%s:%d" % (expected_api_address,
expected_apiserver_port),
actual_api_address)
@patch('magnum.objects.Bay.get_by_uuid')
@patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_k8s_master_url_without_baymodel_apiserver_port(
self,
mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid):
expected_context = 'context'
expected_api_address = 'api_address'
expected_baymodel_id = 'e74c40e0-d825-11e2-a28f-0800200c9a61'
expected_protocol = cfg.CONF.kubernetes.k8s_protocol
expected_apiserver_port = cfg.CONF.kubernetes.k8s_port
resource = self.mock_pod()
resource.bay_uuid = 'bay_uuid'
bay = self.mock_bay()
bay.api_address = expected_api_address
bay.baymodel_id = expected_baymodel_id
baymodel = self.mock_baymodel()
baymodel.apiserver_port = None
mock_bay_get_by_uuid.return_value = bay
mock_baymodel_get_by_uuid.return_value = baymodel
actual_api_address = k8s_conductor._retrieve_k8s_master_url(
expected_context, resource)
self.assertEqual("%s://%s:%d" % (expected_protocol,
expected_api_address,
expected_apiserver_port),
actual_api_address)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_pod_create_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_pod_create_with_success(self):
expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
return_value = mock.MagicMock()
return_value.status = mock.MagicMock()
return_value.status.phase = 'Pending'
@ -145,18 +68,13 @@ class TestK8sConductor(base.TestCase):
self.assertEqual('10.0.0.3', expected_pod.host)
expected_pod.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_pod_create_with_fail(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_pod_create_with_fail(self, mock_literal_eval):
expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.return_value.createPod.side_effect = err
@ -167,19 +85,14 @@ class TestK8sConductor(base.TestCase):
self.assertEqual('failed', expected_pod.status)
expected_pod.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_pod_create_fail_on_existing_pod(
self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
self, mock_literal_eval):
expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=409)
mock_kube_api.return_value.createPod.side_effect = err
@ -190,24 +103,18 @@ class TestK8sConductor(base.TestCase):
self.assertEqual('failed', expected_pod.status)
self.assertFalse(expected_pod.create.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
def test_pod_delete_with_success(self,
mock_pod_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with(
@ -215,24 +122,19 @@ class TestK8sConductor(base.TestCase):
namespaces='default')
mock_pod.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('ast.literal_eval')
def test_pod_delete_with_failure(self, mock_literal_eval,
mock_pod_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.return_value.deletePod.side_effect = err
@ -246,25 +148,20 @@ class TestK8sConductor(base.TestCase):
namespaces='default')
self.assertFalse(mock_pod.destroy.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('ast.literal_eval')
def test_pod_delete_succeeds_when_not_found(
self, mock_literal_eval,
mock_pod_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.return_value.deletePod.side_effect = err
@ -276,37 +173,26 @@ class TestK8sConductor(base.TestCase):
name=mock_pod.name, namespaces='default')
mock_pod.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_service_create_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_service_create_with_success(self):
expected_service = self.mock_service()
expected_service.create = mock.MagicMock()
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_create(self.context, expected_service)
mock_kube_api.return_value.createService.assert_called_once_with(
body=manifest, namespaces='default')
expected_service.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_service_create_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_service_create_with_failure(self, mock_literal_eval):
expected_service = self.mock_service()
expected_service.create = mock.MagicMock()
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.return_value.createService.side_effect = err
@ -319,50 +205,39 @@ class TestK8sConductor(base.TestCase):
body=manifest, namespaces='default')
self.assertFalse(expected_service.create.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Service.get_by_uuid')
def test_service_delete_with_success(
self,
mock_service_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_service = mock.MagicMock()
mock_service.name = 'test-service'
mock_service.uuid = 'test-uuid'
mock_service_get_by_uuid.return_value = mock_service
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_delete(self.context, mock_service.uuid)
mock_kube_api.return_value.deleteService.assert_called_once_with(
name=mock_service.name, namespaces='default')
mock_service.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Service.get_by_uuid')
@patch('ast.literal_eval')
def test_service_delete_with_failure(
self, mock_literal_eval,
mock_service_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_service = mock.MagicMock()
mock_service.name = 'test-service'
mock_service.uuid = 'test-uuid'
mock_service_get_by_uuid.return_value = mock_service
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.return_value.deleteService.side_effect = err
@ -376,25 +251,20 @@ class TestK8sConductor(base.TestCase):
name=mock_service.name, namespaces='default')
self.assertFalse(mock_service.destroy.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Service.get_by_uuid')
@patch('ast.literal_eval')
def test_service_delete_succeeds_when_not_found(
self, mock_literal_eval,
mock_service_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_service = mock.MagicMock()
mock_service.name = 'test-service'
mock_service.uuid = 'test-uuid'
mock_service_get_by_uuid.return_value = mock_service
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.return_value.deleteService.side_effect = err
@ -406,36 +276,25 @@ class TestK8sConductor(base.TestCase):
name=mock_service.name, namespaces='default')
mock_service.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_rc_create_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_rc_create_with_success(self):
expected_rc = self.mock_rc()
expected_rc.create = mock.MagicMock()
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_create({}, expected_rc)
(mock_kube_api.return_value.createReplicationController
.assert_called_once_with(body=manifest, namespaces='default'))
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_rc_create_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_rc_create_with_failure(self, mock_literal_eval):
expected_rc = self.mock_rc()
expected_rc.create = mock.MagicMock()
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
(mock_kube_api.return_value.createReplicationController.side_effect
@ -449,24 +308,18 @@ class TestK8sConductor(base.TestCase):
.assert_called_once_with(body=manifest, namespaces='default'))
self.assertFalse(expected_rc.create.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.ReplicationController.get_by_uuid')
def test_rc_delete_with_success(self,
mock_rc_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_rc = mock.MagicMock()
mock_rc.name = 'test-rc'
mock_rc.uuid = 'test-uuid'
mock_rc_get_by_uuid.return_value = mock_rc
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_delete(self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController
@ -474,24 +327,19 @@ class TestK8sConductor(base.TestCase):
namespaces='default'))
mock_rc.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.ReplicationController.get_by_uuid')
@patch('ast.literal_eval')
def test_rc_delete_with_failure(self, mock_literal_eval,
mock_rc_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_rc = mock.MagicMock()
mock_rc.name = 'test-rc'
mock_rc.uuid = 'test-uuid'
mock_rc_get_by_uuid.return_value = mock_rc
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
(mock_kube_api.return_value.deleteReplicationController.side_effect
@ -507,25 +355,20 @@ class TestK8sConductor(base.TestCase):
namespaces='default'))
self.assertFalse(mock_rc.destroy.called)
@patch('magnum.conductor.handlers.k8s_conductor._object_has_stack')
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.ReplicationController.get_by_uuid')
@patch('ast.literal_eval')
def test_rc_delete_succeeds_when_not_found(
self, mock_literal_eval,
mock_rc_get_by_uuid,
mock_retrieve_k8s_master_url,
mock_object_has_stack):
expected_master_url = 'api_address'
mock_rc = mock.MagicMock()
mock_rc.name = 'test-rc'
mock_rc.uuid = 'test-uuid'
mock_rc_get_by_uuid.return_value = mock_rc
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
(mock_kube_api.return_value.deleteReplicationController.side_effect
@ -539,10 +382,7 @@ class TestK8sConductor(base.TestCase):
namespaces='default'))
self.assertTrue(mock_rc.destroy.called)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_rc_update_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_rc_update_with_success(self):
expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name'
@ -551,10 +391,7 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_update(self.context, expected_rc)
(mock_kube_api.return_value.replaceReplicationController
.assert_called_once_with(body=manifest, name=expected_rc.name,
@ -562,11 +399,8 @@ class TestK8sConductor(base.TestCase):
expected_rc.refresh.assert_called_once_with(self.context)
expected_rc.save.assert_called_once_with()
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_rc_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_rc_update_with_failure(self, mock_literal_eval):
expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name'
@ -574,9 +408,7 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
(mock_kube_api.return_value.replaceReplicationController
@ -591,10 +423,7 @@ class TestK8sConductor(base.TestCase):
namespaces='default'))
self.assertFalse(expected_rc.update.called)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_service_update_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_service_update_with_success(self):
expected_service = self.mock_service()
expected_service.uuid = 'test-uuid'
expected_service.name = 'test-name'
@ -603,10 +432,7 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_update(self.context, expected_service)
mock_kube_api.return_value.replaceService.assert_called_once_with(
body=manifest, name=expected_service.name,
@ -614,20 +440,15 @@ class TestK8sConductor(base.TestCase):
expected_service.refresh.assert_called_once_with(self.context)
expected_service.save.assert_called_once_with()
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_service_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_service_update_with_failure(self, mock_literal_eval):
expected_service = self.mock_service()
expected_service.uuid = 'test-uuid'
expected_service.name = 'test-name'
expected_service.refresh = mock.MagicMock()
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.return_value.replaceService.side_effect = err
@ -641,10 +462,7 @@ class TestK8sConductor(base.TestCase):
namespaces='default')
self.assertFalse(expected_service.refresh.called)
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
def test_pod_update_with_success(self,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_pod_update_with_success(self):
expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
@ -653,10 +471,7 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.pod_update(self.context, expected_pod)
mock_kube_api.return_value.replacePod.assert_called_once_with(
body=manifest, name=expected_pod.name,
@ -664,11 +479,8 @@ class TestK8sConductor(base.TestCase):
expected_pod.refresh.assert_called_once_with(self.context)
expected_pod.save.assert_called_once_with()
@patch('magnum.conductor.handlers.k8s_conductor._retrieve_k8s_master_url')
@patch('ast.literal_eval')
def test_pod_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
def test_pod_update_with_failure(self, mock_literal_eval):
expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
@ -676,9 +488,7 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch('magnum.conductor.handlers.k8s_conductor.Handler.k8s_api',
new_callable=mock.PropertyMock) as mock_kube_api:
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.return_value.replacePod.side_effect = err

View File

@ -0,0 +1,79 @@
# Copyright 2015 Huawei Technologies Co.,LTD.
#
# 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 mock import patch
from oslo_config import cfg
from magnum.conductor import k8s_api
from magnum import objects
from magnum.tests import base
class TestK8sAPI(base.TestCase):
def _test_retrieve_k8s_master_url(self, mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid,
apiserver_port=None):
expected_context = 'context'
expected_api_address = 'api_address'
expected_baymodel_id = 'e74c40e0-d825-11e2-a28f-0800200c9a61'
expected_protocol = cfg.CONF.kubernetes.k8s_protocol
if apiserver_port is None:
expected_apiserver_port = cfg.CONF.kubernetes.k8s_port
else:
expected_apiserver_port = apiserver_port
resource = objects.Pod({})
resource.bay_uuid = 'bay_uuid'
bay = objects.Bay({})
bay.api_address = expected_api_address
bay.baymodel_id = expected_baymodel_id
baymodel = objects.BayModel({})
baymodel.apiserver_port = apiserver_port
mock_bay_get_by_uuid.return_value = bay
mock_baymodel_get_by_uuid.return_value = baymodel
actual_api_address = k8s_api.K8sAPI._retrieve_k8s_master_url(
expected_context, resource)
self.assertEqual("%s://%s:%d" % (expected_protocol,
expected_api_address,
expected_apiserver_port),
actual_api_address)
@patch('magnum.objects.Bay.get_by_uuid')
@patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_k8s_master_url(
self,
mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid):
self._test_retrieve_k8s_master_url(mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid,
apiserver_port=9999)
@patch('magnum.objects.Bay.get_by_uuid')
@patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_k8s_master_url_without_baymodel_apiserver_port(
self,
mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid):
self._test_retrieve_k8s_master_url(mock_baymodel_get_by_uuid,
mock_bay_get_by_uuid)
@patch('magnum.conductor.k8s_api.K8sAPI')
def test_create_k8s_api(self, mock_k8s_api_cls):
context = 'context'
bay = objects.Bay({})
k8s_api.create_k8s_api(context, bay)
mock_k8s_api_cls.assert_called_once_with(context, bay)

View File

@ -0,0 +1,66 @@
# Copyright 2015 Huawei Technologies Co.,LTD.
#
# 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 mock import patch
from magnum.conductor import utils
from magnum import objects
from magnum.tests import base
class TestConductorUtils(base.TestCase):
def _test_retrieve_bay(self, obj, mock_bay_get_by_uuid):
expected_context = 'context'
expected_bay_uuid = 'bay_uuid'
obj.bay_uuid = expected_bay_uuid
utils.retrieve_bay(expected_context, obj)
mock_bay_get_by_uuid.assert_called_once_with(expected_context,
expected_bay_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_pod(self,
mock_bay_get_by_uuid):
self._test_retrieve_bay(objects.Pod({}), mock_bay_get_by_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_service(self,
mock_bay_get_by_uuid):
self._test_retrieve_bay(objects.Service({}), mock_bay_get_by_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_rc(self,
mock_bay_get_by_uuid):
self._test_retrieve_bay(objects.ReplicationController({}),
mock_bay_get_by_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_container(self,
mock_bay_get_by_uuid):
self._test_retrieve_bay(objects.Container({}), mock_bay_get_by_uuid)
@patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_baymodel(self, mock_baymodel_get_by_uuid):
expected_context = 'context'
expected_baymodel_uuid = 'baymodel_uuid'
bay = objects.Bay({})
bay.baymodel_id = expected_baymodel_uuid
utils.retrieve_baymodel(expected_context, bay)
mock_baymodel_get_by_uuid.assert_called_once_with(
expected_context,
expected_baymodel_uuid)