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 # add image to glance
if [[ "$ENABLED_SERVICES" =~ 'm-api' ]]; then 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}" IMAGE_URLS+=",${MANGUM_GUEST_IMAGE_URL}"
fi fi

View File

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

View File

@ -19,6 +19,7 @@ Includes decorator for re-raising Magnum-type exceptions.
""" """
import functools import functools
import json
import sys import sys
import uuid import uuid
@ -464,7 +465,14 @@ class OSDistroFieldNotFound(ResourceNotFound):
class KubernetesAPIFailed(MagnumException): 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') self.__class__.code = kwargs.get('code')
super(KubernetesAPIFailed, self).__init__(message, **kwargs) super(KubernetesAPIFailed, self).__init__(message, **kwargs)

View File

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

View File

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

View File

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

View File

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

View File

@ -37,5 +37,7 @@ cat >> /etc/environment <<EOF
KUBERNETES_MASTER=http://$KUBE_MASTER_IP:8080 KUBERNETES_MASTER=http://$KUBE_MASTER_IP:8080
EOF 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 systemctl enable kube-register

View File

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

View File

@ -20,20 +20,24 @@ write_files:
if [ "$1" = "-u" ]; then if [ "$1" = "-u" ]; then
echo "unregistering minion $myip" echo "unregistering minion $myip"
kubectl -s ${master_url} delete minion/$myip kubectl -s ${master_url} delete node/$myip
else else
echo "registering minion $myip" echo "registering minion $myip"
cpu=$(($(nproc) * 1000)) cpu=$(($(nproc) * 1000))
memory=$(awk '/MemTotal: /{print $2 * 1024}' /proc/meminfo) memory=$(awk '/MemTotal: /{print $2 * 1024}' /proc/meminfo)
cat <<EOF | kubectl create -s ${master_url} -f- cat <<EOF | kubectl create -s ${master_url} -f-
apiVersion: v1beta1 apiVersion: v1
id: $myip id: $myip
kind: Minion kind: Node
resources: resources:
capacity: capacity:
cpu: $cpu cpu: $cpu
memory: $memory memory: $memory
metadata:
name: $myip
spec:
externalID: $myip
EOF EOF
fi fi
- path: /etc/systemd/system/kube-register.service - 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 # First we test Magnum's command line to see if we can stand up
# a baymodel, bay and a pod # a baymodel, bay and a pod
export NIC_ID=$(neutron net-show public | awk '/ id /{print $4}') 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 # pass the appropriate variables via a config file

View File

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

View File

@ -15,13 +15,13 @@
from oslo_config import cfg from oslo_config import cfg
from magnum.common import exception from magnum.common import exception
from magnum.common.pythonk8sclient.swagger_client import rest
from magnum.conductor.handlers import k8s_conductor from magnum.conductor.handlers import k8s_conductor
from magnum import objects from magnum import objects
from magnum.tests import base from magnum.tests import base
import mock import mock
from mock import patch from mock import patch
from six.moves.urllib import error
cfg.CONF.import_opt('k8s_protocol', 'magnum.conductor.handlers.k8s_conductor', 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 = mock.MagicMock()
return_value.status.phase = 'Pending' return_value.status.phase = 'Pending'
return_value.spec = mock.MagicMock() return_value.spec = mock.MagicMock()
return_value.spec.host = '10.0.0.3' return_value.spec.node_name = '10.0.0.3'
mock_kube_api.return_value.createPod.return_value = return_value mock_kube_api.return_value.create_namespaced_pod.return_value = (
return_value)
self.kube_handler.pod_create(self.context, expected_pod) self.kube_handler.pod_create(self.context, expected_pod)
self.assertEqual('Pending', expected_pod.status) self.assertEqual('Pending', expected_pod.status)
self.assertEqual('10.0.0.3', expected_pod.host) self.assertEqual('10.0.0.3', expected_pod.host)
expected_pod.create.assert_called_once_with(self.context) expected_pod.create.assert_called_once_with(self.context)
@patch('ast.literal_eval') def test_pod_create_with_fail(self):
def test_pod_create_with_fail(self, mock_literal_eval):
expected_pod = self.mock_pod() expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock() expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}' expected_pod.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=500)
fp=mock.MagicMock(), code=500) mock_kube_api.return_value.create_namespaced_pod.side_effect = err
mock_kube_api.return_value.createPod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler. self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler.
pod_create, self.context, expected_pod) pod_create, self.context, expected_pod)
self.assertEqual('failed', expected_pod.status) self.assertEqual('failed', expected_pod.status)
expected_pod.create.assert_called_once_with(self.context) expected_pod.create.assert_called_once_with(self.context)
@patch('ast.literal_eval') def test_pod_create_fail_on_existing_pod(self):
def test_pod_create_fail_on_existing_pod(
self, mock_literal_eval):
expected_pod = self.mock_pod() expected_pod = self.mock_pod()
expected_pod.create = mock.MagicMock() expected_pod.create = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}' expected_pod.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=409)
fp=mock.MagicMock(), code=409) mock_kube_api.return_value.create_namespaced_pod.side_effect = err
mock_kube_api.return_value.createPod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler. self.assertRaises(exception.KubernetesAPIFailed, self.kube_handler.
pod_create, self.context, expected_pod) 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.pod_delete(self.context, mock_pod.uuid) self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_pod
name=mock_pod.name, .assert_called_once_with(
namespaces='default') name=mock_pod.name, body={}, namespace='default'))
mock_pod.destroy.assert_called_once_with(self.context) mock_pod.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid') @patch('magnum.objects.Pod.get_by_uuid')
@patch('ast.literal_eval') def test_pod_delete_with_failure(self, mock_pod_get_by_uuid,
def test_pod_delete_with_failure(self, mock_literal_eval,
mock_pod_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_pod = mock.MagicMock() mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod' mock_pod.name = 'test-pod'
@ -135,24 +127,21 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=500)
fp=mock.MagicMock(), code=500) mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
mock_kube_api.return_value.deletePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_delete, self.kube_handler.pod_delete,
self.context, mock_pod.uuid) self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_pod
name=mock_pod.name, .assert_called_once_with(
namespaces='default') name=mock_pod.name, body={}, namespace='default'))
self.assertFalse(mock_pod.destroy.called) self.assertFalse(mock_pod.destroy.called)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid') @patch('magnum.objects.Pod.get_by_uuid')
@patch('ast.literal_eval')
def test_pod_delete_succeeds_when_not_found( def test_pod_delete_succeeds_when_not_found(
self, mock_literal_eval, self,
mock_pod_get_by_uuid, mock_pod_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_pod = mock.MagicMock() mock_pod = mock.MagicMock()
@ -162,15 +151,14 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
mock_kube_api.return_value.deletePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.pod_delete(self.context, mock_pod.uuid) self.kube_handler.pod_delete(self.context, mock_pod.uuid)
mock_kube_api.return_value.deletePod.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_pod
name=mock_pod.name, namespaces='default') .assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
mock_pod.destroy.assert_called_once_with(self.context) mock_pod.destroy.assert_called_once_with(self.context)
def test_service_create_with_success(self): 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_create(self.context, expected_service) self.kube_handler.service_create(self.context, expected_service)
mock_kube_api.return_value.createService.assert_called_once_with( (mock_kube_api.return_value.create_namespaced_service
body=manifest, namespaces='default') .assert_called_once_with(body=manifest, namespace='default'))
expected_service.create.assert_called_once_with(self.context) expected_service.create.assert_called_once_with(self.context)
@patch('ast.literal_eval') def test_service_create_with_failure(self):
def test_service_create_with_failure(self, mock_literal_eval):
expected_service = self.mock_service() expected_service = self.mock_service()
expected_service.create = mock.MagicMock() expected_service.create = mock.MagicMock()
manifest = {"key": "value"} manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}' expected_service.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) (mock_kube_api.return_value.create_namespaced_service
mock_kube_api.return_value.createService.side_effect = err .side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_create, self.kube_handler.service_create,
self.context, expected_service) self.context, expected_service)
mock_kube_api.return_value.createService.assert_called_once_with( (mock_kube_api.return_value.create_namespaced_service
body=manifest, namespaces='default') .assert_called_once_with(body=manifest, namespace='default'))
self.assertFalse(expected_service.create.called) self.assertFalse(expected_service.create.called)
@patch('magnum.conductor.utils.object_has_stack') @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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_delete(self.context, mock_service.uuid) self.kube_handler.service_delete(self.context, mock_service.uuid)
mock_kube_api.return_value.deleteService.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_service
name=mock_service.name, namespaces='default') .assert_called_once_with(
name=mock_service.name, namespace='default'))
mock_service.destroy.assert_called_once_with(self.context) mock_service.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Service.get_by_uuid') @patch('magnum.objects.Service.get_by_uuid')
@patch('ast.literal_eval')
def test_service_delete_with_failure( def test_service_delete_with_failure(
self, mock_literal_eval, self,
mock_service_get_by_uuid, mock_service_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_service = mock.MagicMock() mock_service = mock.MagicMock()
@ -238,24 +224,23 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=500)
fp=mock.MagicMock(), code=500) (mock_kube_api.return_value.delete_namespaced_service
mock_kube_api.return_value.deleteService.side_effect = err .side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_delete, self.kube_handler.service_delete,
self.context, mock_service.uuid) self.context, mock_service.uuid)
mock_kube_api.return_value.deleteService.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_service
name=mock_service.name, namespaces='default') .assert_called_once_with(
name=mock_service.name, namespace='default'))
self.assertFalse(mock_service.destroy.called) self.assertFalse(mock_service.destroy.called)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Service.get_by_uuid') @patch('magnum.objects.Service.get_by_uuid')
@patch('ast.literal_eval')
def test_service_delete_succeeds_when_not_found( def test_service_delete_succeeds_when_not_found(
self, mock_literal_eval, self,
mock_service_get_by_uuid, mock_service_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_service = mock.MagicMock() mock_service = mock.MagicMock()
@ -265,15 +250,15 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) (mock_kube_api.return_value.delete_namespaced_service
mock_kube_api.return_value.deleteService.side_effect = err .side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.service_delete(self.context, mock_service.uuid) self.kube_handler.service_delete(self.context, mock_service.uuid)
mock_kube_api.return_value.deleteService.assert_called_once_with( (mock_kube_api.return_value.delete_namespaced_service
name=mock_service.name, namespaces='default') .assert_called_once_with(
name=mock_service.name, namespace='default'))
mock_service.destroy.assert_called_once_with(self.context) mock_service.destroy.assert_called_once_with(self.context)
def test_rc_create_with_success(self): 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_create({}, expected_rc) self.kube_handler.rc_create({}, expected_rc)
(mock_kube_api.return_value.createReplicationController (mock_kube_api.return_value
.assert_called_once_with(body=manifest, namespaces='default')) .create_namespaced_replication_controller
.assert_called_once_with(body=manifest, namespace='default'))
@patch('ast.literal_eval') def test_rc_create_with_failure(self):
def test_rc_create_with_failure(self, mock_literal_eval):
expected_rc = self.mock_rc() expected_rc = self.mock_rc()
expected_rc.create = mock.MagicMock() expected_rc.create = mock.MagicMock()
manifest = {"key": "value"} manifest = {"key": "value"}
expected_rc.manifest = '{"key": "value"}' expected_rc.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=500)
fp=mock.MagicMock(), code=500) (mock_kube_api.return_value
(mock_kube_api.return_value.createReplicationController.side_effect .create_namespaced_replication_controller.side_effect) = err
) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_create, self.kube_handler.rc_create,
self.context, expected_rc) self.context, expected_rc)
(mock_kube_api.return_value.createReplicationController (mock_kube_api.return_value
.assert_called_once_with(body=manifest, namespaces='default')) .create_namespaced_replication_controller
.assert_called_once_with(body=manifest, namespace='default'))
self.assertFalse(expected_rc.create.called) self.assertFalse(expected_rc.create.called)
@patch('magnum.conductor.utils.object_has_stack') @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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_delete(self.context, mock_rc.uuid) self.kube_handler.rc_delete(self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController (mock_kube_api.return_value
.assert_called_once_with(name=mock_rc.name, .delete_namespaced_replication_controller
namespaces='default')) .assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
mock_rc.destroy.assert_called_once_with(self.context) mock_rc.destroy.assert_called_once_with(self.context)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.ReplicationController.get_by_uuid') @patch('magnum.objects.ReplicationController.get_by_uuid')
@patch('ast.literal_eval') def test_rc_delete_with_failure(self, mock_rc_get_by_uuid,
def test_rc_delete_with_failure(self, mock_literal_eval,
mock_rc_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_rc = mock.MagicMock() mock_rc = mock.MagicMock()
mock_rc.name = 'test-rc' mock_rc.name = 'test-rc'
@ -340,26 +323,24 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=500)
fp=mock.MagicMock(), code=500) (mock_kube_api.return_value
(mock_kube_api.return_value.deleteReplicationController.side_effect .delete_namespaced_replication_controller.side_effect) = err
) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_delete, self.kube_handler.rc_delete,
self.context, mock_rc.uuid) self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController (mock_kube_api.return_value
.assert_called_once_with(name=mock_rc.name, .delete_namespaced_replication_controller
namespaces='default')) .assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
self.assertFalse(mock_rc.destroy.called) self.assertFalse(mock_rc.destroy.called)
@patch('magnum.conductor.utils.object_has_stack') @patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.ReplicationController.get_by_uuid') @patch('magnum.objects.ReplicationController.get_by_uuid')
@patch('ast.literal_eval')
def test_rc_delete_succeeds_when_not_found( def test_rc_delete_succeeds_when_not_found(
self, mock_literal_eval, self,
mock_rc_get_by_uuid, mock_rc_get_by_uuid,
mock_object_has_stack): mock_object_has_stack):
mock_rc = mock.MagicMock() mock_rc = mock.MagicMock()
@ -369,17 +350,16 @@ class TestK8sConductor(base.TestCase):
mock_object_has_stack.return_value = True mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) (mock_kube_api.return_value
(mock_kube_api.return_value.deleteReplicationController.side_effect .delete_namespaced_replication_controller.side_effect) = err
) = err
mock_literal_eval.return_value = {'message': 'error'}
self.kube_handler.rc_delete(self.context, mock_rc.uuid) self.kube_handler.rc_delete(self.context, mock_rc.uuid)
(mock_kube_api.return_value.deleteReplicationController (mock_kube_api.return_value
.assert_called_once_with(name=mock_rc.name, .delete_namespaced_replication_controller
namespaces='default')) .assert_called_once_with(name=mock_rc.name, body={},
namespace='default'))
self.assertTrue(mock_rc.destroy.called) self.assertTrue(mock_rc.destroy.called)
def test_rc_update_with_success(self): 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.rc_update(self.context, expected_rc) 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, .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.refresh.assert_called_once_with(self.context)
expected_rc.save.assert_called_once_with() expected_rc.save.assert_called_once_with()
@patch('ast.literal_eval') def test_rc_update_with_failure(self):
def test_rc_update_with_failure(self, mock_literal_eval):
expected_rc = self.mock_rc() expected_rc = self.mock_rc()
expected_rc.uuid = 'test-uuid' expected_rc.uuid = 'test-uuid'
expected_rc.name = 'test-name' expected_rc.name = 'test-name'
@ -409,18 +389,18 @@ class TestK8sConductor(base.TestCase):
expected_rc.manifest = '{"key": "value"}' expected_rc.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) (mock_kube_api.return_value
(mock_kube_api.return_value.replaceReplicationController .replace_namespaced_replication_controller
.side_effect) = err .side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.rc_update, self.kube_handler.rc_update,
self.context, expected_rc) 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, .assert_called_once_with(body=manifest, name=expected_rc.name,
namespaces='default')) namespace='default'))
self.assertFalse(expected_rc.update.called) self.assertFalse(expected_rc.update.called)
def test_service_update_with_success(self): 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.service_update(self.context, expected_service) 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, body=manifest, name=expected_service.name,
namespaces='default') namespace='default'))
expected_service.refresh.assert_called_once_with(self.context) expected_service.refresh.assert_called_once_with(self.context)
expected_service.save.assert_called_once_with() expected_service.save.assert_called_once_with()
@patch('ast.literal_eval') def test_service_update_with_failure(self):
def test_service_update_with_failure(self, mock_literal_eval):
expected_service = self.mock_service() expected_service = self.mock_service()
expected_service.uuid = 'test-uuid' expected_service.uuid = 'test-uuid'
expected_service.name = 'test-name' expected_service.name = 'test-name'
@ -449,17 +429,17 @@ class TestK8sConductor(base.TestCase):
manifest = {"key": "value"} manifest = {"key": "value"}
expected_service.manifest = '{"key": "value"}' expected_service.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) (mock_kube_api.return_value.replace_namespaced_service
mock_kube_api.return_value.replaceService.side_effect = err .side_effect) = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.service_update, self.kube_handler.service_update,
self.context, expected_service) 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, body=manifest, name=expected_service.name,
namespaces='default') namespace='default'))
self.assertFalse(expected_service.refresh.called) self.assertFalse(expected_service.refresh.called)
def test_pod_update_with_success(self): 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: with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
self.kube_handler.pod_update(self.context, expected_pod) 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, body=manifest, name=expected_pod.name,
namespaces='default') namespace='default'))
expected_pod.refresh.assert_called_once_with(self.context) expected_pod.refresh.assert_called_once_with(self.context)
expected_pod.save.assert_called_once_with() expected_pod.save.assert_called_once_with()
@patch('ast.literal_eval') def test_pod_update_with_failure(self):
def test_pod_update_with_failure(self, mock_literal_eval):
expected_pod = self.mock_pod() expected_pod = self.mock_pod()
expected_pod.uuid = 'test-uuid' expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name' expected_pod.name = 'test-name'
@ -489,15 +469,14 @@ class TestK8sConductor(base.TestCase):
expected_pod.manifest = '{"key": "value"}' expected_pod.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') 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', err = rest.ApiException(status=404)
fp=mock.MagicMock(), code=404) mock_kube_api.return_value.replace_namespaced_pod.side_effect = err
mock_kube_api.return_value.replacePod.side_effect = err
mock_literal_eval.return_value = {'message': 'error'}
self.assertRaises(exception.KubernetesAPIFailed, self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_update, self.kube_handler.pod_update,
self.context, expected_pod) 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, body=manifest, name=expected_pod.name,
namespaces='default') namespace='default'))
self.assertFalse(expected_pod.refresh.called) self.assertFalse(expected_pod.refresh.called)

View File

@ -32,11 +32,11 @@ class TestScaleManager(base.TestCase):
pods = list() pods = list()
for h in pod_hosts: for h in pod_hosts:
pod = mock.MagicMock() pod = mock.MagicMock()
pod.spec.host = h pod.spec.node_name = h
pods.append(pod) pods.append(pod)
mock_k8s_api = mock.MagicMock() 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_create_k8s_api.return_value = mock_k8s_api
mock_heat_output = mock.MagicMock() mock_heat_output = mock.MagicMock()

View File

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