Migrate to Kubernetes Release 1

Kubernetes Release 1.0 was announced at OSCON

A new Fedora Atomic image has been built with version 1.0.4 and
this series of patches will update the templates, scripts, documents
to work with the new image.

The api has also been changed from v1beta3 to v1 and the beta
api is no longer available, so the interface between Magnum and
Kubernetes master are updated as well in this series of patches.

This particular patch will bring up a V1 cluster with the
bay-create command.  Because the switch to the V1 API requires
all the code changes to be applied at once, this patch pulls
in portion of 3 patches together.  The changes include:

1. devstack plugin downloads the new image.

2. k8s conductor and other code calls the new V1 client and
k8s methods.

3. Configuration for k8s services and docker updated with
new parameters.

4. Minion registration and example code updated to V1.

5. Functional tests updated to V1.

6. Gate test setup points to the new image.

Co-Authored-By: Hongbin Lu <hongbin.lu@huawei.com>

Change-Id: I046931ad491e8b7ee45943852901eac5c3df913e
Partially-Implements: blueprint kubernetes-v1
This commit is contained in:
Ton Ngo 2015-07-30 04:15:07 +00:00 committed by Eli Qiao
parent 27db916c5e
commit 4bfed61fe3
16 changed files with 242 additions and 260 deletions

View File

@ -18,7 +18,7 @@ if is_service_enabled m-api m-cond; then
# add image to glance
if [[ "$ENABLED_SERVICES" =~ 'm-api' ]]; then
MANGUM_GUEST_IMAGE_URL=${MANGUM_GUEST_IMAGE_URL:-"https://fedorapeople.org/groups/magnum/fedora-21-atomic-3.qcow2"}
MANGUM_GUEST_IMAGE_URL=${MANGUM_GUEST_IMAGE_URL:-"https://fedorapeople.org/groups/magnum/fedora-21-atomic-5.qcow2"}
IMAGE_URLS+=",${MANGUM_GUEST_IMAGE_URL}"
fi

View File

@ -7,5 +7,5 @@ user = admin
tenant = admin
pass = secrete
[magnum]
image_id = fedora-21-atomic-3
image_id = fedora-21-atomic-5
nic_id = public

View File

@ -19,6 +19,7 @@ Includes decorator for re-raising Magnum-type exceptions.
"""
import functools
import json
import sys
import uuid
@ -464,7 +465,14 @@ class OSDistroFieldNotFound(ResourceNotFound):
class KubernetesAPIFailed(MagnumException):
def __init__(self, message=None, **kwargs):
def __init__(self, message=None, err=None, **kwargs):
if err:
if err.body:
message = json.loads(err.body)['message']
else:
message = err.reason
self.__class__.code = err.status
else:
self.__class__.code = kwargs.get('code')
super(KubernetesAPIFailed, self).__init__(message, **kwargs)

View File

@ -187,7 +187,7 @@ class ApiClient(object):
"""
if isinstance(obj, type(None)):
return None
elif isinstance(obj, (str, int, float, bool, tuple)):
elif isinstance(obj, (unicode, str, int, float, bool, tuple, file)):
return obj
elif isinstance(obj, list):
return [self.sanitize_for_serialization(sub_obj)

View File

@ -16,12 +16,11 @@ from oslo_log import log as logging
from magnum.common import exception
from magnum.common import k8s_manifest
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.conductor import k8s_api as k8s
from magnum.conductor import utils as conductor_utils
from magnum import objects
import ast
from six.moves.urllib import error
LOG = logging.getLogger(__name__)
@ -41,11 +40,10 @@ class Handler(object):
self.k8s_api = k8s.create_k8s_api(context, service)
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)
self.k8s_api.create_namespaced_service(body=manifest,
namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
# call the service object to persist in db
service.create(context)
return service
@ -55,12 +53,11 @@ class Handler(object):
self.k8s_api = k8s.create_k8s_api(context, service)
manifest = k8s_manifest.parse(service.manifest)
try:
self.k8s_api.replaceService(name=service.name,
self.k8s_api.replace_namespaced_service(name=str(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)
namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
# call the service object to persist in db
service.refresh(context)
service.save()
@ -72,15 +69,13 @@ class Handler(object):
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')
except error.HTTPError as err:
if err.code == 404:
self.k8s_api.delete_namespaced_service(name=str(service.name),
namespace='default')
except rest.ApiException as err:
if err.status == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
raise exception.KubernetesAPIFailed(err=err)
# call the service object to persist in db
service.destroy(context)
@ -90,15 +85,15 @@ class Handler(object):
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')
except error.HTTPError as err:
resp = self.k8s_api.create_namespaced_pod(body=manifest,
namespace='default')
except rest.ApiException as err:
pod.status = 'failed'
if err.code != 409:
if err.status != 409:
pod.create(context)
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
raise exception.KubernetesAPIFailed(err=err)
pod.status = resp.status.phase
pod.host = resp.spec.host
pod.host = resp.spec.node_name
# call the pod object to persist in db
# TODO(yuanying): parse pod file and,
# - extract pod name and set it
@ -112,11 +107,11 @@ class Handler(object):
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,
namespaces='default')
except error.HTTPError as err:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code, message=message)
self.k8s_api.replace_namespaced_pod(name=str(pod.name),
body=manifest,
namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
# call the pod object to persist in db
pod.refresh(context)
pod.save()
@ -128,15 +123,13 @@ class Handler(object):
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')
except error.HTTPError as err:
if err.code == 404:
self.k8s_api.delete_namespaced_pod(name=str(pod.name), body={},
namespace='default')
except rest.ApiException as err:
if err.status == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
raise exception.KubernetesAPIFailed(err=err)
# call the pod object to persist in db
pod.destroy(context)
@ -146,11 +139,10 @@ class Handler(object):
self.k8s_api = k8s.create_k8s_api(context, rc)
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)
self.k8s_api.create_namespaced_replication_controller(
body=manifest, namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
# call the rc object to persist in db
rc.create(context)
return rc
@ -160,12 +152,10 @@ class Handler(object):
self.k8s_api = k8s.create_k8s_api(context, rc)
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)
self.k8s_api.replace_namespaced_replication_controller(
name=str(rc.name), body=manifest, namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
# call the rc object to persist in db
rc.refresh(context)
rc.save()
@ -177,14 +167,12 @@ class Handler(object):
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')
except error.HTTPError as err:
if err.code == 404:
self.k8s_api.delete_namespaced_replication_controller(
name=str(rc.name), body={}, namespace='default')
except rest.ApiException as err:
if err.status == 404:
pass
else:
message = ast.literal_eval(err.read())['message']
raise exception.KubernetesAPIFailed(code=err.code,
message=message)
raise exception.KubernetesAPIFailed(err=err)
# call the rc object to persist in db
rc.destroy(context)

View File

@ -15,8 +15,8 @@
from oslo_config import cfg
from magnum.common import config
from magnum.common.pythonk8sclient.client import ApivbetaApi
from magnum.common.pythonk8sclient.client import swagger
from magnum.common.pythonk8sclient.swagger_client import api_client
from magnum.common.pythonk8sclient.swagger_client.apis import apiv_api
from magnum.conductor import utils
from magnum.i18n import _
@ -35,14 +35,14 @@ kubernetes_opts = [
cfg.CONF.register_opts(kubernetes_opts, group='kubernetes')
class K8sAPI(ApivbetaApi.ApivbetaApi):
class K8sAPI(apiv_api.ApivApi):
def __init__(self, context, obj):
# retrieve the URL of the k8s API endpoint
k8s_api_endpoint = self._retrieve_k8s_api_endpoint(context, obj)
# build a connection with Kubernetes master
client = swagger.ApiClient(k8s_api_endpoint)
client = api_client.ApiClient(k8s_api_endpoint)
super(K8sAPI, self).__init__(client)

View File

@ -48,8 +48,8 @@ class ScaleManager(object):
hosts_no_container = list(hosts)
k8s_api = k8s.create_k8s_api(self.context, bay)
for pod in k8s_api.listPod().items:
host = pod.spec.host
for pod in k8s_api.list_namespaced_pod(namespace='default').items:
host = pod.spec.node_name
if host in hosts_no_container:
hosts_no_container.remove(host)

View File

@ -13,9 +13,10 @@ sed -i '
sed -i '
/^KUBE_API_ADDRESS=/ s/=.*/="--address=0.0.0.0"/
/^KUBE_SERVICE_ADDRESSES=/ s|=.*|="--portal_net='"$PORTAL_NETWORK_CIDR"'"|
/^KUBE_API_ARGS=/ s/=.*/="--runtime_config=api\/v1beta3"/
/^KUBE_SERVICE_ADDRESSES=/ s|=.*|="--service-cluster-ip-range='"$PORTAL_NETWORK_CIDR"'"|
/^KUBE_API_ARGS=/ s/=.*/="--runtime_config=api\/all=true"/
/^KUBE_ETCD_SERVERS=/ s/=.*/="--etcd_servers=http:\/\/127.0.0.1:2379"/
/^KUBE_ADMISSION_CONTROL=/ s/=.*/=""/
' /etc/kubernetes/apiserver
sed -i '

View File

@ -37,5 +37,7 @@ cat >> /etc/environment <<EOF
KUBERNETES_MASTER=http://$KUBE_MASTER_IP:8080
EOF
sed -i '/^DOCKER_STORAGE_OPTIONS=/ s/=.*/=--storage-driver devicemapper --storage-opt dm.fs=xfs --storage-opt dm.thinpooldev=\/dev\/mapper\/docker-docker--pool --storage-opt dm.use_deferred_removal=true/' /etc/sysconfig/docker-storage
systemctl enable kube-register

View File

@ -5,15 +5,13 @@ write_files:
owner: "root:root"
permissions: "0644"
content: |
apiVersion: v1
kind: Pod
apiVersion: v1beta1
id: web
metadata:
labels:
name: web
desiredState:
manifest:
version: v1beta1
id: web
name: web
spec:
containers:
- name: web
image: larsks/thttpd
@ -23,10 +21,13 @@ write_files:
owner: "root:root"
permissions: "0644"
content: |
apiVersion: v1
kind: Service
apiVersion: v1beta1
id: web
port: 8000
metadata:
name: web
spec:
ports:
- port: 8000
selector:
name: web
containerPort: 80

View File

@ -20,20 +20,24 @@ write_files:
if [ "$1" = "-u" ]; then
echo "unregistering minion $myip"
kubectl -s ${master_url} delete minion/$myip
kubectl -s ${master_url} delete node/$myip
else
echo "registering minion $myip"
cpu=$(($(nproc) * 1000))
memory=$(awk '/MemTotal: /{print $2 * 1024}' /proc/meminfo)
cat <<EOF | kubectl create -s ${master_url} -f-
apiVersion: v1beta1
apiVersion: v1
id: $myip
kind: Minion
kind: Node
resources:
capacity:
cpu: $cpu
memory: $memory
metadata:
name: $myip
spec:
externalID: $myip
EOF
fi
- path: /etc/systemd/system/kube-register.service

View File

@ -60,7 +60,7 @@ popd
# First we test Magnum's command line to see if we can stand up
# a baymodel, bay and a pod
export NIC_ID=$(neutron net-show public | awk '/ id /{print $4}')
export IMAGE_ID=$(glance --os-image-api-version 1 image-show fedora-21-atomic-3 | awk '/ id /{print $4}')
export IMAGE_ID=$(glance --os-image-api-version 1 image-show fedora-21-atomic-5 | awk '/ id /{print $4}')
# pass the appropriate variables via a config file

View File

@ -25,8 +25,8 @@ import time
import fixtures
from magnum.common.pythonk8sclient.client import ApivbetaApi
from magnum.common.pythonk8sclient.client import swagger
from magnum.common.pythonk8sclient.swagger_client import api_client
from magnum.common.pythonk8sclient.swagger_client.apis import apiv_api
from magnum.tests import base
from magnumclient.openstack.common.apiclient import exceptions
from magnumclient.openstack.common import cliutils
@ -214,8 +214,8 @@ class TestKubernetesAPIs(BaseMagnumClient):
cls.bay = cls._create_bay('testk8sAPI', cls.baymodel.uuid)
kube_api_address = cls.cs.bays.get(cls.bay.uuid).api_address
kube_api_url = 'http://%s' % kube_api_address
k8s_client = swagger.ApiClient(kube_api_url)
cls.k8s_api = ApivbetaApi.ApivbetaApi(k8s_client)
k8s_client = api_client.ApiClient(kube_api_url)
cls.k8s_api = apiv_api.ApivApi(k8s_client)
@classmethod
def tearDownClass(cls):
@ -230,53 +230,52 @@ class TestKubernetesAPIs(BaseMagnumClient):
cls._delete_baymodel(cls.baymodel.uuid)
def test_pod_apis(self):
pod_manifest = {'apiVersion': 'v1beta3',
pod_manifest = {'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {'color': 'blue', 'name': 'test'},
'spec': {'containers': [{'image': 'dockerfile/redis',
'name': 'redis'}]}}
resp = self.k8s_api.createPod(body=pod_manifest, namespaces='default')
self.assertEqual(resp.metadata['name'], 'test')
resp = self.k8s_api.create_namespaced_pod(body=pod_manifest,
namespace='default')
self.assertEqual(resp.metadata.name, 'test')
self.assertTrue(resp.status.phase)
resp = self.k8s_api.readPod(name='test', namespaces='default')
self.assertEqual(resp.metadata['name'], 'test')
resp = self.k8s_api.read_namespaced_pod(name='test',
namespace='default')
self.assertEqual(resp.metadata.name, 'test')
self.assertTrue(resp.status.phase)
resp = self.k8s_api.deletePod(name='test', namespaces='default')
self.assertFalse(resp.phase)
resp = self.k8s_api.delete_namespaced_pod(name='test', body={},
namespace='default')
def test_service_apis(self):
service_manifest = {'apiVersion': 'v1beta3',
service_manifest = {'apiVersion': 'v1',
'kind': 'Service',
'metadata': {'labels': {'name': 'frontend'},
'name': 'frontend',
'resourceversion': 'v1beta3'},
'resourceversion': 'v1'},
'spec': {'ports': [{'port': 80,
'protocol': 'TCP',
'targetPort': 80}],
'selector': {'name': 'frontend'}}}
resp = self.k8s_api.createService(body=service_manifest,
namespaces='default')
self.assertEqual(resp.metadata['name'], 'frontend')
resp = self.k8s_api.create_namespaced_service(body=service_manifest,
namespace='default')
self.assertEqual(resp.metadata.name, 'frontend')
self.assertTrue(resp.status)
resp = self.k8s_api.readService(name='frontend', namespaces='default')
self.assertEqual(resp.metadata['name'], 'frontend')
resp = self.k8s_api.read_namespaced_service(name='frontend',
namespace='default')
self.assertEqual(resp.metadata.name, 'frontend')
self.assertTrue(resp.status)
resp = self.k8s_api.deleteService(name='frontend',
namespaces='default')
# TODO(madhuri) Currently the V1beta3_ServiceStatus
# has no attribute defined. Uncomment this assertion
# when the class is redefined to contain 'phase'.
# self.assertTrue(resp.phase)
resp = self.k8s_api.delete_namespaced_service(name='frontend',
namespace='default')
def test_replication_controller_apis(self):
rc_manifest = {
'apiVersion': 'v1beta3',
'apiVersion': 'v1',
'kind': 'ReplicationController',
'metadata': {'labels': {'name': 'frontend'},
'name': 'frontend'},
@ -290,16 +289,15 @@ class TestKubernetesAPIs(BaseMagnumClient):
'ports': [{'containerPort': 80,
'protocol': 'TCP'}]}]}}}}
resp = self.k8s_api.createReplicationController(body=rc_manifest,
namespaces='default')
self.assertEqual(resp.metadata['name'], 'frontend')
resp = self.k8s_api.create_namespaced_replication_controller(
body=rc_manifest, namespace='default')
self.assertEqual(resp.metadata.name, 'frontend')
self.assertEqual(resp.spec.replicas, 2)
resp = self.k8s_api.readReplicationController(name='frontend',
namespaces='default')
self.assertEqual(resp.metadata['name'], 'frontend')
resp = self.k8s_api.read_namespaced_replication_controller(
name='frontend', namespace='default')
self.assertEqual(resp.metadata.name, 'frontend')
self.assertEqual(resp.spec.replicas, 2)
resp = self.k8s_api.deleteReplicationController(name='frontend',
namespaces='default')
self.assertFalse(resp.replicas)
resp = self.k8s_api.delete_namespaced_replication_controller(
name='frontend', body={}, namespace='default')

View File

@ -15,13 +15,13 @@
from oslo_config import cfg
from magnum.common import exception
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.conductor.handlers import k8s_conductor
from magnum import objects
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.k8s_conductor',
@ -60,43 +60,37 @@ class TestK8sConductor(base.TestCase):
return_value.status = mock.MagicMock()
return_value.status.phase = 'Pending'
return_value.spec = mock.MagicMock()
return_value.spec.host = '10.0.0.3'
mock_kube_api.return_value.createPod.return_value = return_value
return_value.spec.node_name = '10.0.0.3'
mock_kube_api.return_value.create_namespaced_pod.return_value = (
return_value)
self.kube_handler.pod_create(self.context, expected_pod)
self.assertEqual('Pending', expected_pod.status)
self.assertEqual('10.0.0.3', expected_pod.host)
expected_pod.create.assert_called_once_with(self.context)
@patch('ast.literal_eval')
def test_pod_create_with_fail(self, mock_literal_eval):
def test_pod_create_with_fail(self):
expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=500)
mock_kube_api.return_value.create_namespaced_pod.side_effect = err
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('ast.literal_eval')
def test_pod_create_fail_on_existing_pod(
self, mock_literal_eval):
def test_pod_create_fail_on_existing_pod(self):
expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=409)
mock_kube_api.return_value.create_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler.
pod_create, self.context, expected_pod)
@ -117,16 +111,14 @@ class TestK8sConductor(base.TestCase):
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(
name=mock_pod.name,
namespaces='default')
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
mock_pod.destroy.assert_called_once_with(self.context)
@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,
def test_pod_delete_with_failure(self, mock_pod_get_by_uuid,
mock_object_has_stack):
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
@ -135,24 +127,21 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=500)
mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_delete,
self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with(
name=mock_pod.name,
namespaces='default')
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
self.assertFalse(mock_pod.destroy.called)
@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,
self,
mock_pod_get_by_uuid,
mock_object_has_stack):
mock_pod = mock.MagicMock()
@ -162,15 +151,14 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with(
name=mock_pod.name, namespaces='default')
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
mock_pod.destroy.assert_called_once_with(self.context)
def test_service_create_with_success(self):
@ -181,28 +169,26 @@ class TestK8sConductor(base.TestCase):
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')
(mock_kube_api.return_value.create_namespaced_service
.assert_called_once_with(body=manifest, namespace='default'))
expected_service.create.assert_called_once_with(self.context)
@patch('ast.literal_eval')
def test_service_create_with_failure(self, mock_literal_eval):
def test_service_create_with_failure(self):
expected_service = self.mock_service()
expected_service.create = mock.MagicMock()
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
(mock_kube_api.return_value.create_namespaced_service
.side_effect) = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_create,
self.context, expected_service)
mock_kube_api.return_value.createService.assert_called_once_with(
body=manifest, namespaces='default')
(mock_kube_api.return_value.create_namespaced_service
.assert_called_once_with(body=manifest, namespace='default'))
self.assertFalse(expected_service.create.called)
@patch('magnum.conductor.utils.object_has_stack')
@ -220,15 +206,15 @@ class TestK8sConductor(base.TestCase):
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_kube_api.return_value.delete_namespaced_service
.assert_called_once_with(
name=mock_service.name, namespace='default'))
mock_service.destroy.assert_called_once_with(self.context)
@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,
self,
mock_service_get_by_uuid,
mock_object_has_stack):
mock_service = mock.MagicMock()
@ -238,24 +224,23 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=500)
(mock_kube_api.return_value.delete_namespaced_service
.side_effect) = err
self.assertRaises(exception.KubernetesAPIFailed,
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_kube_api.return_value.delete_namespaced_service
.assert_called_once_with(
name=mock_service.name, namespace='default'))
self.assertFalse(mock_service.destroy.called)
@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,
self,
mock_service_get_by_uuid,
mock_object_has_stack):
mock_service = mock.MagicMock()
@ -265,15 +250,15 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
(mock_kube_api.return_value.delete_namespaced_service
.side_effect) = err
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_kube_api.return_value.delete_namespaced_service
.assert_called_once_with(
name=mock_service.name, namespace='default'))
mock_service.destroy.assert_called_once_with(self.context)
def test_rc_create_with_success(self):
@ -284,28 +269,27 @@ class TestK8sConductor(base.TestCase):
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'))
(mock_kube_api.return_value
.create_namespaced_replication_controller
.assert_called_once_with(body=manifest, namespace='default'))
@patch('ast.literal_eval')
def test_rc_create_with_failure(self, mock_literal_eval):
def test_rc_create_with_failure(self):
expected_rc = self.mock_rc()
expected_rc.create = mock.MagicMock()
manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}'
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
) = err
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=500)
(mock_kube_api.return_value
.create_namespaced_replication_controller.side_effect) = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_create,
self.context, expected_rc)
(mock_kube_api.return_value.createReplicationController
.assert_called_once_with(body=manifest, namespaces='default'))
(mock_kube_api.return_value
.create_namespaced_replication_controller
.assert_called_once_with(body=manifest, namespace='default'))
self.assertFalse(expected_rc.create.called)
@patch('magnum.conductor.utils.object_has_stack')
@ -322,16 +306,15 @@ class TestK8sConductor(base.TestCase):
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
.assert_called_once_with(name=mock_rc.name,
namespaces='default'))
(mock_kube_api.return_value
.delete_namespaced_replication_controller
.assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
mock_rc.destroy.assert_called_once_with(self.context)
@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,
def test_rc_delete_with_failure(self, mock_rc_get_by_uuid,
mock_object_has_stack):
mock_rc = mock.MagicMock()
mock_rc.name = 'test-rc'
@ -340,26 +323,24 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
) = err
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=500)
(mock_kube_api.return_value
.delete_namespaced_replication_controller.side_effect) = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_delete,
self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController
.assert_called_once_with(name=mock_rc.name,
namespaces='default'))
(mock_kube_api.return_value
.delete_namespaced_replication_controller
.assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
self.assertFalse(mock_rc.destroy.called)
@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,
self,
mock_rc_get_by_uuid,
mock_object_has_stack):
mock_rc = mock.MagicMock()
@ -369,17 +350,16 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True
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
) = err
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
(mock_kube_api.return_value
.delete_namespaced_replication_controller.side_effect) = err
self.kube_handler.rc_delete(self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController
.assert_called_once_with(name=mock_rc.name,
namespaces='default'))
(mock_kube_api.return_value
.delete_namespaced_replication_controller
.assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
self.assertTrue(mock_rc.destroy.called)
def test_rc_update_with_success(self):
@ -393,14 +373,14 @@ class TestK8sConductor(base.TestCase):
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
(mock_kube_api.return_value
.replace_namespaced_replication_controller
.assert_called_once_with(body=manifest, name=expected_rc.name,
namespaces='default'))
namespace='default'))
expected_rc.refresh.assert_called_once_with(self.context)
expected_rc.save.assert_called_once_with()
@patch('ast.literal_eval')
def test_rc_update_with_failure(self, mock_literal_eval):
def test_rc_update_with_failure(self):
expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name'
@ -409,18 +389,18 @@ class TestK8sConductor(base.TestCase):
expected_rc.manifest = '{"key": "value"}'
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
err = rest.ApiException(status=404)
(mock_kube_api.return_value
.replace_namespaced_replication_controller
.side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_update,
self.context, expected_rc)
(mock_kube_api.return_value.replaceReplicationController
(mock_kube_api.return_value
.replace_namespaced_replication_controller
.assert_called_once_with(body=manifest, name=expected_rc.name,
namespaces='default'))
namespace='default'))
self.assertFalse(expected_rc.update.called)
def test_service_update_with_success(self):
@ -434,14 +414,14 @@ class TestK8sConductor(base.TestCase):
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(
(mock_kube_api.return_value.replace_namespaced_service
.assert_called_once_with(
body=manifest, name=expected_service.name,
namespaces='default')
namespace='default'))
expected_service.refresh.assert_called_once_with(self.context)
expected_service.save.assert_called_once_with()
@patch('ast.literal_eval')
def test_service_update_with_failure(self, mock_literal_eval):
def test_service_update_with_failure(self):
expected_service = self.mock_service()
expected_service.uuid = 'test-uuid'
expected_service.name = 'test-name'
@ -449,17 +429,17 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}'
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
(mock_kube_api.return_value.replace_namespaced_service
.side_effect) = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_update,
self.context, expected_service)
mock_kube_api.return_value.replaceService.assert_called_once_with(
(mock_kube_api.return_value.replace_namespaced_service
.assert_called_once_with(
body=manifest, name=expected_service.name,
namespaces='default')
namespace='default'))
self.assertFalse(expected_service.refresh.called)
def test_pod_update_with_success(self):
@ -473,14 +453,14 @@ class TestK8sConductor(base.TestCase):
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(
(mock_kube_api.return_value.replace_namespaced_pod
.assert_called_once_with(
body=manifest, name=expected_pod.name,
namespaces='default')
namespace='default'))
expected_pod.refresh.assert_called_once_with(self.context)
expected_pod.save.assert_called_once_with()
@patch('ast.literal_eval')
def test_pod_update_with_failure(self, mock_literal_eval):
def test_pod_update_with_failure(self):
expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
@ -489,15 +469,14 @@ class TestK8sConductor(base.TestCase):
expected_pod.manifest = '{"key": "value"}'
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
mock_literal_eval.return_value = {'message': 'error'}
err = rest.ApiException(status=404)
mock_kube_api.return_value.replace_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_update,
self.context, expected_pod)
mock_kube_api.return_value.replacePod.assert_called_once_with(
(mock_kube_api.return_value.replace_namespaced_pod
.assert_called_once_with(
body=manifest, name=expected_pod.name,
namespaces='default')
namespace='default'))
self.assertFalse(expected_pod.refresh.called)

View File

@ -32,11 +32,11 @@ class TestScaleManager(base.TestCase):
pods = list()
for h in pod_hosts:
pod = mock.MagicMock()
pod.spec.host = h
pod.spec.node_name = h
pods.append(pod)
mock_k8s_api = mock.MagicMock()
mock_k8s_api.listPod.return_value.items = pods
mock_k8s_api.list_namespaced_pod.return_value.items = pods
mock_create_k8s_api.return_value = mock_k8s_api
mock_heat_output = mock.MagicMock()

View File

@ -46,3 +46,4 @@ six>=1.9.0
stevedore>=1.5.0 # Apache-2.0
taskflow>=1.16.0
cryptography>=1.0 # Apache-2.0
urllib3>=1.8.3