Added support of Kubernetes API in magnum.

This patch removes the use of Kubernetes CLI i.e kubectl
from magnum and adds the support of Kubernetes API.

Change-Id: Ibe7354abba962dde6855225471f468f6a19a41ff
Partially-Implements: blueprint python-k8sclient
This commit is contained in:
Madhuri Kumari 2015-05-12 19:26:46 +09:00
parent 027a0a3cb9
commit 52c532560a
4 changed files with 369 additions and 140 deletions

View File

@ -458,3 +458,9 @@ class ImageNotAuthorized(MagnumException):
class OSDistroFieldNotFound(ResourceNotFound):
message = _("Image %(image_id)s doesn't contain os-distro field.")
class KubernetesAPIFailed(MagnumException):
def __init__(self, message=None, **kwargs):
self.__class__.code = kwargs.get('code')
super(KubernetesAPIFailed, self).__init__(message, **kwargs)

View File

@ -139,7 +139,7 @@ class ApiClient(object):
"""
if isinstance(obj, type(None)):
return None
elif isinstance(obj, (str, int, long, float, bool, file)):
elif isinstance(obj, (unicode, str, int, long, float, bool, file)):
return obj
elif isinstance(obj, list):
return [ApiClient.sanitizeForSerialization(subObj) for subObj in obj]

View File

@ -16,12 +16,15 @@ from oslo_config import cfg
from magnum.common import clients
from magnum.common import exception
from magnum.conductor.handlers.common import kube_utils
from magnum.common import k8s_manifest
from magnum.common.pythonk8sclient.client import ApivbetaApi
from magnum.common.pythonk8sclient.client import swagger
from magnum import objects
from magnum.openstack.common._i18n import _
from magnum.openstack.common._i18n import _LW
from magnum.openstack.common import log as logging
import ast
from six.moves.urllib import error
LOG = logging.getLogger(__name__)
@ -89,15 +92,38 @@ class Handler(object):
def __init__(self):
super(Handler, self).__init__()
self.kube_cli = kube_utils.KubeClient()
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
"""
if self._k8s_api is None:
# 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)
# trigger a kubectl command
status = self.kube_cli.service_create(k8s_master_url, service)
if not status:
return None
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(service.manifest)
try:
self.k8s_api.createService(body=manifest,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
# call the service object to persist in db
service.create(context)
return service
@ -105,10 +131,15 @@ 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)
# trigger a kubectl command
status = self.kube_cli.service_update(k8s_master_url, service)
if not status:
return None
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(service.manifest)
try:
self.k8s_api.replaceService(name=service.name,
body=manifest,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
# call the service object to persist in db
service.refresh(context)
service.save()
@ -118,11 +149,18 @@ class Handler(object):
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):
# trigger a kubectl command
status = self.kube_cli.service_delete(k8s_master_url, service.name)
if not status:
return None
try:
self.k8s_api.deleteService(name=service.name,
namespaces='default')
except error.HTTPError as err:
if err.code == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
# call the service object to persist in db
service.destroy(context)
@ -130,13 +168,17 @@ class Handler(object):
def pod_create(self, context, pod):
LOG.debug("pod_create")
k8s_master_url = _retrieve_k8s_master_url(context, pod)
# trigger a kubectl command
status = self.kube_cli.pod_create(k8s_master_url, pod)
# TODO(yuanying): Is this correct location of updating status?
if not status:
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(pod.manifest)
try:
resp = self.k8s_api.createPod(body=manifest, namespaces='default')
except error.HTTPError as err:
pod.status = 'failed'
else:
pod.status = 'pending'
if err.code != 409:
pod.create(context)
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
pod.status = resp['status']['phase']
# call the pod object to persist in db
# TODO(yuanying): parse pod file and,
# - extract pod name and set it
@ -149,10 +191,14 @@ 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)
# trigger a kubectl command
status = self.kube_cli.pod_update(k8s_master_url, pod)
if not status:
return None
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(pod.manifest)
try:
self.k8s_api.replacePod(name=pod.name, body=manifest,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
# call the pod object to persist in db
pod.refresh(context)
pod.save()
@ -163,16 +209,18 @@ class Handler(object):
# trigger a kubectl command
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):
try:
status = self.kube_cli.pod_delete(k8s_master_url, pod.name)
if not status:
return None
except exception.PodNotFound:
msg = _LW("Pod '%s' not found on bay, "
"continuing to delete from database.")
LOG.warn(msg, uuid)
self.k8s_api.deletePod(name=pod.name,
namespaces='default')
except error.HTTPError as err:
if err.code == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
# call the pod object to persist in db
pod.destroy(context)
@ -180,10 +228,14 @@ class Handler(object):
def rc_create(self, context, rc):
LOG.debug("rc_create")
k8s_master_url = _retrieve_k8s_master_url(context, rc)
# trigger a kubectl command
status = self.kube_cli.rc_create(k8s_master_url, rc)
if not status:
return None
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(rc.manifest)
try:
self.k8s_api.createReplicationController(body=manifest,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
# call the rc object to persist in db
rc.create(context)
return rc
@ -191,10 +243,15 @@ 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)
# trigger a kubectl command
status = self.kube_cli.rc_update(k8s_master_url, rc)
if not status:
return None
self.k8s_api = k8s_master_url
manifest = k8s_manifest.parse(rc.manifest)
try:
self.k8s_api.replaceReplicationController(name=rc.name,
body=manifest,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
# call the rc object to persist in db
rc.refresh(context)
rc.save()
@ -204,10 +261,17 @@ class Handler(object):
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):
# trigger a kubectl command
status = self.kube_cli.rc_delete(k8s_master_url, rc.name)
if not status:
return None
try:
self.k8s_api.deleteReplicationController(name=rc.name,
namespaces='default')
except error.HTTPError as err:
if err.code == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
# call the rc object to persist in db
rc.destroy(context)

View File

@ -21,6 +21,7 @@ from magnum.tests import base
import mock
from mock import patch
from six.moves.urllib import error
cfg.CONF.import_opt('k8s_protocol', 'magnum.conductor.handlers.kube',
@ -125,30 +126,59 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_create.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
mock_kube_api.createPod.return_value = {'status':
{'phase': 'Pending'}}
self.kube_handler.pod_create(self.context, expected_pod)
self.assertEqual('pending', expected_pod.status)
self.assertEqual('Pending', expected_pod.status)
expected_pod.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_pod_create_with_fail(self,
@patch('ast.literal_eval')
def test_pod_create_with_fail(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_create.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.createPod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.pod_create(self.context, expected_pod)
self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler.
pod_create, self.context, expected_pod)
self.assertEqual('failed', expected_pod.status)
expected_pod.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._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'
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.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=409)
mock_kube_api.createPod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler.
pod_create, self.context, expected_pod)
self.assertEqual('failed', expected_pod.status)
self.assertFalse(expected_pod.create.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@patch('magnum.objects.Pod.get_by_uuid')
@ -164,19 +194,19 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_delete.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_cli.pod_delete.assert_called_once_with(
expected_master_url, mock_pod.name)
mock_kube_api.deletePod.assert_called_once_with(name=mock_pod.name,
namespaces='default')
mock_pod.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@patch('magnum.objects.Pod.get_by_uuid')
def test_pod_delete_with_failure(self,
@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):
@ -188,19 +218,24 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_delete.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.deletePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_cli.pod_delete.assert_called_once_with(
expected_master_url, mock_pod.name)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_delete,
self.context, mock_pod.uuid)
mock_kube_api.deletePod.assert_called_once_with(name=mock_pod.name,
namespaces='default')
self.assertFalse(mock_pod.destroy.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@patch('magnum.objects.Pod.get_by_uuid')
def test_pod_delete_succeeds_when_not_found(self,
@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):
@ -212,13 +247,16 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_delete.side_effect = exception.PodNotFound()
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.deletePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_cli.pod_delete.assert_called_once_with(
expected_master_url, mock_pod.name)
mock_kube_api.deletePod.assert_called_once_with(
name=mock_pod.name, namespaces='default')
mock_pod.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@ -227,30 +265,39 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_create.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.service_create(self.context, expected_service)
mock_kube_cli.service_create.assert_called_once_with(
expected_master_url, expected_service)
mock_kube_api.createService.assert_called_once_with(
body=manifest, namespaces='default')
expected_service.create.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_service_create_with_failure(self,
@patch('ast.literal_eval')
def test_service_create_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_create.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.createService.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.service_create(self.context, expected_service)
mock_kube_cli.service_create.assert_called_once_with(
expected_master_url, expected_service)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_create,
self.context, expected_service)
mock_kube_api.createService.assert_called_once_with(
body=manifest, namespaces='default')
self.assertFalse(expected_service.create.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@ -268,19 +315,19 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_delete.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.service_delete(self.context, mock_service.uuid)
mock_kube_cli.service_delete.assert_called_once_with(
expected_master_url, mock_service.name)
mock_kube_api.deleteService.assert_called_once_with(
name=mock_service.name, namespaces='default')
mock_service.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@patch('magnum.objects.Service.get_by_uuid')
def test_service_delete_with_failure(self,
@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):
@ -292,14 +339,47 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_delete.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.deleteService.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_delete,
self.context, mock_service.uuid)
mock_kube_api.deleteService.assert_called_once_with(
name=mock_service.name, namespaces='default')
self.assertFalse(mock_service.destroy.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@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.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.deleteService.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.service_delete(self.context, mock_service.uuid)
mock_kube_cli.service_delete.assert_called_once_with(
expected_master_url, mock_service.name)
self.assertFalse(mock_service.destroy.called)
mock_kube_api.deleteService.assert_called_once_with(
name=mock_service.name, namespaces='default')
mock_service.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_rc_create_with_success(self,
@ -307,29 +387,38 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_create.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.rc_create({}, expected_rc)
mock_kube_cli.rc_create.assert_called_once_with(
expected_master_url, expected_rc)
mock_kube_api.createReplicationController.assert_called_once_with(
body=manifest, namespaces='default')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_rc_create_with_failure(self,
@patch('ast.literal_eval')
def test_rc_create_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_create.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.createReplicationController.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.rc_create(self.context, expected_rc)
mock_kube_cli.rc_create.assert_called_once_with(
expected_master_url, expected_rc)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_create,
self.context, expected_rc)
mock_kube_api.createReplicationController.assert_called_once_with(
body=manifest, namespaces='default')
self.assertFalse(expected_rc.create.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@ -347,19 +436,19 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_delete.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.rc_delete(self.context, mock_rc.uuid)
mock_kube_cli.rc_delete.assert_called_once_with(
expected_master_url, mock_rc.name)
mock_kube_api.deleteReplicationController.assert_called_once_with(
name=mock_rc.name, namespaces='default')
mock_rc.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@patch('magnum.objects.ReplicationController.get_by_uuid')
def test_rc_delete_with_failure(self,
@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):
@ -371,14 +460,47 @@ class TestKube(base.TestCase):
mock_retrieve_k8s_master_url.return_value = expected_master_url
mock_object_has_stack.return_value = True
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_delete.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=500)
mock_kube_api.deleteReplicationController.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_delete,
self.context, mock_rc.uuid)
mock_kube_api.deleteReplicationController.assert_called_once_with(
name=mock_rc.name, namespaces='default')
self.assertFalse(mock_rc.destroy.called)
@patch('magnum.conductor.handlers.kube._object_has_stack')
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@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.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.deleteReplicationController.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.rc_delete(self.context, mock_rc.uuid)
mock_kube_cli.rc_delete.assert_called_once_with(
expected_master_url, mock_rc.name)
self.assertFalse(mock_rc.destroy.called)
mock_kube_api.deleteReplicationController.assert_called_once_with(
name=mock_rc.name, namespaces='default')
self.assertTrue(mock_rc.destroy.called)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_rc_update_with_success(self,
@ -386,34 +508,46 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name'
expected_rc.refresh = mock.MagicMock()
expected_rc.save = mock.MagicMock()
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_update.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.rc_update(self.context, expected_rc)
mock_kube_cli.rc_update.assert_called_once_with(
expected_master_url, expected_rc)
mock_kube_api.replaceReplicationController.assert_called_once_with(
body=manifest, name=expected_rc.name, namespaces='default')
expected_rc.refresh.assert_called_once_with(self.context)
expected_rc.save.assert_called_once_with()
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_rc_update_with_failure(self,
@patch('ast.literal_eval')
def test_rc_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name'
expected_rc.update = mock.MagicMock()
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.rc_update.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.replaceReplicationController.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.rc_update(self.context, expected_rc)
mock_kube_cli.rc_update.assert_called_once_with(
expected_master_url, expected_rc)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_update,
self.context, expected_rc)
mock_kube_api.replaceReplicationController.assert_called_once_with(
body=manifest, name=expected_rc.name,
namespaces='default')
self.assertFalse(expected_rc.update.called)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@ -422,34 +556,46 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
expected_service = self.mock_service()
expected_service.uuid = 'test-uuid'
expected_service.name = 'test-name'
expected_service.refresh = mock.MagicMock()
expected_service.save = mock.MagicMock()
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_update.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.service_update(self.context, expected_service)
mock_kube_cli.service_update.assert_called_once_with(
expected_master_url, expected_service)
mock_kube_api.replaceService.assert_called_once_with(
body=manifest, name=expected_service.name,
namespaces='default')
expected_service.refresh.assert_called_once_with(self.context)
expected_service.save.assert_called_once_with()
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_service_update_with_failure(self,
@patch('ast.literal_eval')
def test_service_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
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.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.service_update.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.replaceService.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.service_update(self.context, expected_service)
mock_kube_cli.service_update.assert_called_once_with(
expected_master_url, expected_service)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_update,
self.context, expected_service)
mock_kube_api.replaceService.assert_called_once_with(
body=manifest, name=expected_service.name,
namespaces='default')
self.assertFalse(expected_service.refresh.called)
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
@ -458,32 +604,45 @@ class TestKube(base.TestCase):
expected_master_url = 'api_address'
expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
expected_pod.refresh = mock.MagicMock()
expected_pod.save = mock.MagicMock()
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_update.return_value = True
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
self.kube_handler.pod_update(self.context, expected_pod)
mock_kube_cli.pod_update.assert_called_once_with(
expected_master_url, expected_pod)
mock_kube_api.replacePod.assert_called_once_with(
body=manifest, name=expected_pod.name,
namespaces='default')
expected_pod.refresh.assert_called_once_with(self.context)
expected_pod.save.assert_called_once_with()
@patch('magnum.conductor.handlers.kube._retrieve_k8s_master_url')
def test_pod_update_with_failure(self,
@patch('ast.literal_eval')
def test_pod_update_with_failure(self, mock_literal_eval,
mock_retrieve_k8s_master_url):
expected_master_url = 'api_address'
expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
expected_pod.refresh = mock.MagicMock()
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
mock_retrieve_k8s_master_url.return_value = expected_master_url
with patch.object(self.kube_handler, 'kube_cli') as mock_kube_cli:
mock_kube_cli.pod_update.return_value = False
with patch.object(self.kube_handler, '_k8s_api') as mock_kube_api:
err = error.HTTPError(url='fake', msg='fake', hdrs='fake',
fp=mock.MagicMock(), code=404)
mock_kube_api.replacePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.pod_update(self.context, expected_pod)
mock_kube_cli.pod_update.assert_called_once_with(
expected_master_url, expected_pod)
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_update,
self.context, expected_pod)
mock_kube_api.replacePod.assert_called_once_with(
body=manifest, name=expected_pod.name,
namespaces='default')
self.assertFalse(expected_pod.refresh.called)