Merge "Implements k8s resource creation/updating with data"
This commit is contained in:
commit
c22e2fa328
@ -12,12 +12,74 @@
|
||||
|
||||
"""Magnum Kubernetes RPC handler."""
|
||||
|
||||
import tempfile
|
||||
|
||||
from magnum.openstack.common import log as logging
|
||||
from magnum.openstack.common import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _extract_resource_type(resource):
|
||||
return resource.__class__.__name__.lower()
|
||||
|
||||
|
||||
def _extract_resource_data(resource):
|
||||
resource_type = _extract_resource_type(resource)
|
||||
data_attribute = "%s_data" % resource_type
|
||||
return getattr(resource, data_attribute, None)
|
||||
|
||||
|
||||
def _extract_resource_definition_url(resource):
|
||||
resource_type = _extract_resource_type(resource)
|
||||
definition_url_attribute = "%s_definition_url" % resource_type
|
||||
return getattr(resource, definition_url_attribute, None)
|
||||
|
||||
|
||||
def _k8s_create(master_address, resource):
|
||||
data = _extract_resource_data(resource)
|
||||
definition_url = _extract_resource_definition_url(resource)
|
||||
if data is not None:
|
||||
return _k8s_create_with_data(master_address, data)
|
||||
else:
|
||||
return _k8s_create_with_path(master_address, definition_url)
|
||||
|
||||
|
||||
def _k8s_create_with_path(master_address, resource_file):
|
||||
return utils.trycmd('kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', resource_file)
|
||||
|
||||
|
||||
def _k8s_create_with_data(master_address, resource_data):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(resource_data)
|
||||
f.flush()
|
||||
return _k8s_create_with_path(master_address, f.name)
|
||||
|
||||
|
||||
def _k8s_update(master_address, resource):
|
||||
data = _extract_resource_data(resource)
|
||||
definition_url = _extract_resource_definition_url(resource)
|
||||
if data is not None:
|
||||
return _k8s_update_with_data(master_address, data)
|
||||
else:
|
||||
return _k8s_update_with_path(master_address, definition_url)
|
||||
|
||||
|
||||
def _k8s_update_with_path(master_address, resource_file):
|
||||
return utils.trycmd('kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', resource_file)
|
||||
|
||||
|
||||
def _k8s_update_with_data(master_address, resource_data):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(resource_data)
|
||||
f.flush()
|
||||
return _k8s_update_with_path(master_address, f.name)
|
||||
|
||||
|
||||
class KubeClient(object):
|
||||
"""These are the backend operations. They are executed by the backend
|
||||
service. API calls via AMQP (within the ReST API) trigger the
|
||||
@ -33,14 +95,8 @@ class KubeClient(object):
|
||||
def service_create(self, master_address, service):
|
||||
LOG.debug("service_create with contents %s" % service)
|
||||
try:
|
||||
if service.service_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', service.service_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo service | kubectl', 'create',
|
||||
'-f', '-')
|
||||
out, err = _k8s_create(master_address, service)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
@ -52,15 +108,8 @@ class KubeClient(object):
|
||||
def service_update(self, master_address, service):
|
||||
LOG.debug("service_update with contents %s" % service)
|
||||
try:
|
||||
if service.service_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', service.service_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo service | kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', '-')
|
||||
out, err = _k8s_update(master_address, service)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
@ -117,43 +166,29 @@ class KubeClient(object):
|
||||
return None
|
||||
|
||||
# Pod Operations
|
||||
def pod_create(self, master_address, contents):
|
||||
LOG.debug("pod_create contents %s" % contents)
|
||||
def pod_create(self, master_address, pod):
|
||||
LOG.debug("pod_create contents %s" % pod)
|
||||
try:
|
||||
if contents.pod_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', contents.pod_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo contents | kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', '-')
|
||||
out, err = _k8s_create(master_address, pod)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
LOG.error("Couldn't create pod with contents %s due to error %s"
|
||||
% (contents, e))
|
||||
% (pod, e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def pod_update(self, master_address, contents):
|
||||
LOG.debug("pod_update contents %s" % contents)
|
||||
def pod_update(self, master_address, pod):
|
||||
LOG.debug("pod_update contents %s" % pod)
|
||||
try:
|
||||
if contents.pod_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', contents.pod_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo contents | kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', '-')
|
||||
out, err = _k8s_update(master_address, pod)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
LOG.error("Couldn't update pod with contents %s due to error %s"
|
||||
% (contents, e))
|
||||
% (pod, e))
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -202,43 +237,29 @@ class KubeClient(object):
|
||||
return None
|
||||
|
||||
# Replication Controller Operations
|
||||
def rc_create(self, master_address, contents):
|
||||
LOG.debug("rc_create contents %s" % contents)
|
||||
def rc_create(self, master_address, rc):
|
||||
LOG.debug("rc_create contents %s" % rc)
|
||||
try:
|
||||
if contents.rc_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', contents.rc_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo contents | kubectl', 'create',
|
||||
'-s', master_address,
|
||||
'-f', '-')
|
||||
out, err = _k8s_create(master_address, rc)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
LOG.error("Couldn't create rc with contents %s due to error %s"
|
||||
% (contents, e))
|
||||
% (rc, e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def rc_update(self, master_address, contents):
|
||||
LOG.debug("rc_update contents %s" % contents)
|
||||
def rc_update(self, master_address, rc):
|
||||
LOG.debug("rc_update contents %s" % rc)
|
||||
try:
|
||||
if contents.rc_definition_url:
|
||||
out, err = utils.trycmd('kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', contents.rc_definition_url)
|
||||
else:
|
||||
# TODO(jay-lau-513) Translate the contents to a json stdin
|
||||
out, err = utils.trycmd('echo contents | kubectl', 'update',
|
||||
'-s', master_address,
|
||||
'-f', '-')
|
||||
out, err = _k8s_update(master_address, rc)
|
||||
|
||||
if err:
|
||||
return False
|
||||
except Exception as e:
|
||||
LOG.error("Couldn't update rc with contents %s due to error %s"
|
||||
% (contents, e))
|
||||
% (rc, e))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
0
magnum/tests/conductor/handlers/common/__init__.py
Normal file
0
magnum/tests/conductor/handlers/common/__init__.py
Normal file
201
magnum/tests/conductor/handlers/common/test_kube_utils.py
Normal file
201
magnum/tests/conductor/handlers/common/test_kube_utils.py
Normal file
@ -0,0 +1,201 @@
|
||||
# Copyright 2014 NEC Corporation. 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 magnum.conductor.handlers.common import kube_utils
|
||||
from magnum import objects
|
||||
from magnum.tests import base
|
||||
|
||||
import mock
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestKubeUtils(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestKubeUtils, self).setUp()
|
||||
|
||||
def test_extract_resource_type(self):
|
||||
expected_resource_type = 'bay'
|
||||
|
||||
bay = objects.Bay({})
|
||||
actual_type = kube_utils._extract_resource_type(bay)
|
||||
self.assertEqual(expected_resource_type, actual_type)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_type')
|
||||
def test_extract_resource_data_with_data(self,
|
||||
mock_extract_resource_type):
|
||||
expected_data = 'expected_data'
|
||||
|
||||
mock_extract_resource_type.return_value = 'mock_type'
|
||||
mock_resource = mock.MagicMock()
|
||||
mock_resource.mock_type_data = expected_data
|
||||
actual_data = kube_utils._extract_resource_data(mock_resource)
|
||||
self.assertEqual(expected_data, actual_data)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_type')
|
||||
def test_extract_resource_definition_url(self,
|
||||
mock_extract_resource_type):
|
||||
expected_data = 'expected_url'
|
||||
|
||||
mock_extract_resource_type.return_value = 'mock_type'
|
||||
mock_resource = mock.MagicMock()
|
||||
mock_resource.mock_type_definition_url = expected_data
|
||||
actual_data = kube_utils._extract_resource_definition_url(
|
||||
mock_resource)
|
||||
self.assertEqual(expected_data, actual_data)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_create_with_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_definition_url')
|
||||
def test_k8s_create_data(self,
|
||||
mock_definition_url,
|
||||
mock_data,
|
||||
mock_create_with_data):
|
||||
expected_data = 'data'
|
||||
master_address = 'master_address'
|
||||
mock_data.return_value = expected_data
|
||||
mock_definition_url.return_value = None
|
||||
mock_resource = mock.MagicMock()
|
||||
|
||||
kube_utils._k8s_create(master_address, mock_resource)
|
||||
mock_create_with_data.assert_called_once_with(master_address,
|
||||
expected_data)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_create_with_path')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_definition_url')
|
||||
def test_k8s_create_url(self,
|
||||
mock_definition_url,
|
||||
mock_data,
|
||||
mock_create_with_path):
|
||||
expected_url = 'url'
|
||||
master_address = 'master_address'
|
||||
mock_data.return_value = None
|
||||
mock_definition_url.return_value = expected_url
|
||||
mock_resource = mock.MagicMock()
|
||||
|
||||
kube_utils._k8s_create(master_address, mock_resource)
|
||||
mock_create_with_path.assert_called_once_with(master_address,
|
||||
expected_url)
|
||||
|
||||
@patch('magnum.openstack.common.utils.trycmd')
|
||||
def test_k8s_create_with_path(self, mock_trycmd):
|
||||
expected_master_address = 'master_address'
|
||||
expected_pod_file = 'pod_file'
|
||||
expected_command = [
|
||||
'kubectl', 'create',
|
||||
'-s', expected_master_address,
|
||||
'-f', expected_pod_file
|
||||
]
|
||||
|
||||
kube_utils._k8s_create_with_path(expected_master_address,
|
||||
expected_pod_file)
|
||||
mock_trycmd.assert_called_once_with(*expected_command)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_create_with_path')
|
||||
@patch('tempfile.NamedTemporaryFile')
|
||||
def test_k8s_create_with_data(self,
|
||||
mock_named_tempfile,
|
||||
mock_k8s_create):
|
||||
expected_master_address = 'master_address'
|
||||
expected_data = 'resource_data'
|
||||
expected_filename = 'resource_file'
|
||||
|
||||
mock_file = mock.MagicMock()
|
||||
mock_file.name = expected_filename
|
||||
mock_named_tempfile.return_value.__enter__.return_value = mock_file
|
||||
|
||||
kube_utils._k8s_create_with_data(expected_master_address,
|
||||
expected_data)
|
||||
|
||||
mock_file.write.assert_called_once_with(expected_data)
|
||||
mock_k8s_create.assert_called_once_with(expected_master_address,
|
||||
expected_filename)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_update_with_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_definition_url')
|
||||
def test_k8s_update_data(self,
|
||||
mock_definition_url,
|
||||
mock_data,
|
||||
mock_update_with_data):
|
||||
expected_data = 'data'
|
||||
master_address = 'master_address'
|
||||
mock_data.return_value = expected_data
|
||||
mock_definition_url.return_value = None
|
||||
mock_resource = mock.MagicMock()
|
||||
|
||||
kube_utils._k8s_update(master_address, mock_resource)
|
||||
mock_update_with_data.assert_called_once_with(master_address,
|
||||
expected_data)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_update_with_path')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_data')
|
||||
@patch('magnum.conductor.handlers.common.kube_utils.'
|
||||
'_extract_resource_definition_url')
|
||||
def test_k8s_update_url(self,
|
||||
mock_definition_url,
|
||||
mock_data,
|
||||
mock_update_with_path):
|
||||
expected_url = 'url'
|
||||
master_address = 'master_address'
|
||||
mock_data.return_value = None
|
||||
mock_definition_url.return_value = expected_url
|
||||
mock_resource = mock.MagicMock()
|
||||
|
||||
kube_utils._k8s_update(master_address, mock_resource)
|
||||
mock_update_with_path.assert_called_once_with(master_address,
|
||||
expected_url)
|
||||
|
||||
@patch('magnum.openstack.common.utils.trycmd')
|
||||
def test_k8s_update_with_path(self, mock_trycmd):
|
||||
expected_master_address = 'master_address'
|
||||
expected_pod_file = 'pod_file'
|
||||
expected_command = [
|
||||
'kubectl', 'update',
|
||||
'-s', expected_master_address,
|
||||
'-f', expected_pod_file
|
||||
]
|
||||
|
||||
kube_utils._k8s_update_with_path(expected_master_address,
|
||||
expected_pod_file)
|
||||
mock_trycmd.assert_called_once_with(*expected_command)
|
||||
|
||||
@patch('magnum.conductor.handlers.common.kube_utils._k8s_update_with_path')
|
||||
@patch('tempfile.NamedTemporaryFile')
|
||||
def test_k8s_update_with_data(self,
|
||||
mock_named_tempfile,
|
||||
mock_k8s_update):
|
||||
expected_master_address = 'master_address'
|
||||
expected_data = 'resource_data'
|
||||
expected_filename = 'resource_file'
|
||||
|
||||
mock_file = mock.MagicMock()
|
||||
mock_file.name = expected_filename
|
||||
mock_named_tempfile.return_value.__enter__.return_value = mock_file
|
||||
|
||||
kube_utils._k8s_update_with_data(expected_master_address,
|
||||
expected_data)
|
||||
|
||||
mock_file.write.assert_called_once_with(expected_data)
|
||||
mock_k8s_update.assert_called_once_with(expected_master_address,
|
||||
expected_filename)
|
Loading…
Reference in New Issue
Block a user