Merge "Objects from Bay - Replication Controller"
This commit is contained in:
commit
ad4e792a2d
|
@ -210,17 +210,7 @@ class ReplicationControllersController(rest.RestController):
|
|||
|
||||
limit = api_utils.validate_limit(limit)
|
||||
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
||||
|
||||
marker_obj = None
|
||||
if marker:
|
||||
marker_obj = objects.ReplicationController.get_by_uuid(
|
||||
pecan.request.context,
|
||||
marker)
|
||||
|
||||
rcs = pecan.request.rpcapi.rc_list(
|
||||
pecan.request.context, limit,
|
||||
marker_obj, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
rcs = pecan.request.rpcapi.rc_list(pecan.request.context, bay_ident)
|
||||
|
||||
return ReplicationControllerCollection.convert_with_links(
|
||||
rcs, limit,
|
||||
|
@ -279,7 +269,8 @@ class ReplicationControllersController(rest.RestController):
|
|||
:param rc_ident: UUID or logical name of a ReplicationController.
|
||||
:param bay_ident: UUID or logical name of the Bay.
|
||||
"""
|
||||
rpc_rc = api_utils.get_rpc_resource('ReplicationController', rc_ident)
|
||||
context = pecan.request.context
|
||||
rpc_rc = pecan.request.rpcapi.rc_show(context, rc_ident, bay_ident)
|
||||
return ReplicationController.convert_with_links(rpc_rc)
|
||||
|
||||
@policy.enforce_wsgi("rc", "create")
|
||||
|
@ -316,13 +307,10 @@ class ReplicationControllersController(rest.RestController):
|
|||
:param bay_ident: UUID or logical name of the Bay.
|
||||
:param patch: a json PATCH document to apply to this rc.
|
||||
"""
|
||||
rpc_rc = api_utils.get_rpc_resource('ReplicationController', rc_ident)
|
||||
# Init manifest and manifest_url field because we don't store them
|
||||
# in database.
|
||||
rpc_rc['manifest'] = None
|
||||
rpc_rc['manifest_url'] = None
|
||||
rc_dict = {}
|
||||
rc_dict['manifest'] = None
|
||||
rc_dict['manifest_url'] = None
|
||||
try:
|
||||
rc_dict = rpc_rc.as_dict()
|
||||
rc = ReplicationController(**api_utils.apply_jsonpatch(rc_dict,
|
||||
patch))
|
||||
if rc.manifest or rc.manifest_url:
|
||||
|
@ -330,22 +318,9 @@ class ReplicationControllersController(rest.RestController):
|
|||
except api_utils.JSONPATCH_EXCEPTIONS as e:
|
||||
raise exception.PatchError(patch=patch, reason=e)
|
||||
|
||||
# Update only the fields that have changed
|
||||
for field in objects.ReplicationController.fields:
|
||||
try:
|
||||
patch_val = getattr(rc, field)
|
||||
except AttributeError:
|
||||
# Ignore fields that aren't exposed in the API
|
||||
continue
|
||||
if patch_val == wtypes.Unset:
|
||||
patch_val = None
|
||||
if rpc_rc[field] != patch_val:
|
||||
rpc_rc[field] = patch_val
|
||||
|
||||
if rc.manifest or rc.manifest_url:
|
||||
pecan.request.rpcapi.rc_update(rpc_rc)
|
||||
else:
|
||||
rpc_rc.save()
|
||||
rpc_rc = pecan.request.rpcapi.rc_update(rc_ident,
|
||||
bay_ident,
|
||||
rc.manifest)
|
||||
return ReplicationController.convert_with_links(rpc_rc)
|
||||
|
||||
@policy.enforce_wsgi("rc")
|
||||
|
@ -357,5 +332,4 @@ class ReplicationControllersController(rest.RestController):
|
|||
:param rc_ident: UUID or logical name of a ReplicationController.
|
||||
:param bay_ident: UUID or logical name of the Bay.
|
||||
"""
|
||||
rpc_rc = api_utils.get_rpc_resource('ReplicationController', rc_ident)
|
||||
pecan.request.rpcapi.rc_delete(rpc_rc.uuid)
|
||||
pecan.request.rpcapi.rc_delete(rc_ident, bay_ident)
|
||||
|
|
|
@ -72,7 +72,7 @@ def is_docker_library_version_atleast(version):
|
|||
def docker_for_container(context, container):
|
||||
if magnum_utils.is_uuid_like(container):
|
||||
container = objects.Container.get_by_uuid(context, container)
|
||||
bay = conductor_utils.retrieve_bay(context, container)
|
||||
bay = conductor_utils.retrieve_bay(context, container.bay_uuid)
|
||||
with docker_for_bay(context, bay) as docker:
|
||||
yield docker
|
||||
|
||||
|
|
|
@ -412,6 +412,16 @@ class ReplicationControllerAlreadyExists(Conflict):
|
|||
message = _("A ReplicationController with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class ReplicationControllerListNotFound(ResourceNotFound):
|
||||
message = _("ReplicationController list could not be found"
|
||||
" for Bay %(bay_uuid)s.")
|
||||
|
||||
|
||||
class ReplicationControllerCreationFailed(Invalid):
|
||||
message = _("ReplicationController creation failed"
|
||||
" for Bay %(bay_uuid)s.")
|
||||
|
||||
|
||||
class ServiceNotFound(ResourceNotFound):
|
||||
message = _("Service %(service)s could not be found.")
|
||||
|
||||
|
|
|
@ -102,18 +102,19 @@ class API(rpc_service.API):
|
|||
def rc_create(self, rc):
|
||||
return self._call('rc_create', rc=rc)
|
||||
|
||||
def rc_update(self, rc):
|
||||
return self._call('rc_update', rc=rc)
|
||||
def rc_update(self, rc_ident, bay_ident, manifest):
|
||||
return self._call('rc_update', rc_ident=rc_ident,
|
||||
bay_ident=bay_ident, manifest=manifest)
|
||||
|
||||
def rc_list(self, context, limit, marker, sort_key, sort_dir):
|
||||
return objects.ReplicationController.list(context, limit, marker,
|
||||
sort_key, sort_dir)
|
||||
def rc_list(self, context, bay_ident):
|
||||
return self._call('rc_list', bay_ident=bay_ident)
|
||||
|
||||
def rc_delete(self, uuid):
|
||||
return self._call('rc_delete', uuid=uuid)
|
||||
def rc_delete(self, rc_ident, bay_ident):
|
||||
return self._call('rc_delete', rc_ident=rc_ident, bay_ident=bay_ident)
|
||||
|
||||
def rc_show(self, context, uuid):
|
||||
return objects.ReplicationController.get_by_uuid(context, uuid)
|
||||
def rc_show(self, context, rc_ident, bay_ident):
|
||||
return self._call('rc_show', rc_ident=rc_ident,
|
||||
bay_ident=bay_ident)
|
||||
|
||||
# Container operations
|
||||
|
||||
|
|
|
@ -16,10 +16,13 @@ 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.common import utils
|
||||
from magnum.conductor import k8s_api as k8s
|
||||
from magnum.conductor import utils as conductor_utils
|
||||
from magnum import objects
|
||||
|
||||
import ast
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -136,43 +139,154 @@ class Handler(object):
|
|||
# Replication Controller Operations
|
||||
def rc_create(self, context, rc):
|
||||
LOG.debug("rc_create")
|
||||
self.k8s_api = k8s.create_k8s_api(context, rc)
|
||||
self.k8s_api = k8s.create_k8s_api_rc(context, rc.bay_uuid)
|
||||
manifest = k8s_manifest.parse(rc.manifest)
|
||||
try:
|
||||
self.k8s_api.create_namespaced_replication_controller(
|
||||
body=manifest, namespace='default')
|
||||
resp = 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)
|
||||
|
||||
if resp is None:
|
||||
raise exception.ReplicationControllerCreationFailed(
|
||||
bay_uuid=rc.bay_uuid)
|
||||
|
||||
rc['uuid'] = resp.metadata.uid
|
||||
rc['name'] = resp.metadata.name
|
||||
rc['images'] = [c.image for c in resp.spec.template.spec.containers]
|
||||
rc['labels'] = ast.literal_eval(resp.metadata.labels)
|
||||
rc['replicas'] = resp.status.replicas
|
||||
return rc
|
||||
|
||||
def rc_update(self, context, rc):
|
||||
LOG.debug("rc_update %s", rc.uuid)
|
||||
self.k8s_api = k8s.create_k8s_api(context, rc)
|
||||
manifest = k8s_manifest.parse(rc.manifest)
|
||||
def rc_update(self, context, rc_ident, bay_ident, manifest):
|
||||
LOG.debug("rc_update %s", rc_ident)
|
||||
# Since bay identifier is specified verify whether its a UUID
|
||||
# or Name. If name is specified as bay identifier need to extract
|
||||
# the bay uuid since its needed to get the k8s_api object.
|
||||
if not utils.is_uuid_like(bay_ident):
|
||||
bay = objects.Bay.get_by_name(context, bay_ident)
|
||||
bay_ident = bay.uuid
|
||||
|
||||
bay_uuid = bay_ident
|
||||
self.k8s_api = k8s.create_k8s_api_rc(context, bay_uuid)
|
||||
if utils.is_uuid_like(rc_ident):
|
||||
rc = objects.ReplicationController.get_by_uuid(context, rc_ident,
|
||||
bay_uuid,
|
||||
self.k8s_api)
|
||||
else:
|
||||
rc = objects.ReplicationController.get_by_name(context, rc_ident,
|
||||
bay_uuid,
|
||||
self.k8s_api)
|
||||
try:
|
||||
self.k8s_api.replace_namespaced_replication_controller(
|
||||
name=str(rc.name), body=manifest, namespace='default')
|
||||
resp = 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()
|
||||
|
||||
if resp is None:
|
||||
raise exception.ReplicationControllerNotFound(rc=rc.uuid)
|
||||
|
||||
rc['uuid'] = resp.metadata.uid
|
||||
rc['name'] = resp.metadata.name
|
||||
rc['project_id'] = context.project_id
|
||||
rc['user_id'] = context.user_id
|
||||
rc['images'] = [c.image for c in resp.spec.template.spec.containers]
|
||||
rc['bay_uuid'] = bay_uuid
|
||||
rc['labels'] = ast.literal_eval(resp.metadata.labels)
|
||||
rc['replicas'] = resp.status.replicas
|
||||
|
||||
return rc
|
||||
|
||||
def rc_delete(self, context, uuid):
|
||||
LOG.debug("rc_delete %s", uuid)
|
||||
rc = objects.ReplicationController.get_by_uuid(context, uuid)
|
||||
self.k8s_api = k8s.create_k8s_api(context, rc)
|
||||
if conductor_utils.object_has_stack(context, rc):
|
||||
def rc_delete(self, context, rc_ident, bay_ident):
|
||||
LOG.debug("rc_delete %s", rc_ident)
|
||||
# Since bay identifier is specified verify whether its a UUID
|
||||
# or Name. If name is specified as bay identifier need to extract
|
||||
# the bay uuid since its needed to get the k8s_api object.
|
||||
if not utils.is_uuid_like(bay_ident):
|
||||
bay = objects.Bay.get_by_name(context, bay_ident)
|
||||
bay_ident = bay.uuid
|
||||
|
||||
bay_uuid = bay_ident
|
||||
self.k8s_api = k8s.create_k8s_api_rc(context, bay_uuid)
|
||||
if utils.is_uuid_like(rc_ident):
|
||||
rc = objects.ReplicationController.get_by_uuid(context, rc_ident,
|
||||
bay_uuid,
|
||||
self.k8s_api)
|
||||
rc_name = rc.name
|
||||
else:
|
||||
rc_name = rc_ident
|
||||
if conductor_utils.object_has_stack(context, bay_uuid):
|
||||
try:
|
||||
self.k8s_api.delete_namespaced_replication_controller(
|
||||
name=str(rc.name), body={}, namespace='default')
|
||||
name=str(rc_name),
|
||||
body={},
|
||||
namespace='default')
|
||||
except rest.ApiException as err:
|
||||
if err.status == 404:
|
||||
pass
|
||||
else:
|
||||
raise exception.KubernetesAPIFailed(err=err)
|
||||
# call the rc object to persist in db
|
||||
rc.destroy(context)
|
||||
|
||||
def rc_show(self, context, rc_ident, bay_ident):
|
||||
LOG.debug("rc_show %s", rc_ident)
|
||||
# Since bay identifier is specified verify whether its a UUID
|
||||
# or Name. If name is specified as bay identifier need to extract
|
||||
# the bay uuid since its needed to get the k8s_api object.
|
||||
if not utils.is_uuid_like(bay_ident):
|
||||
bay = objects.Bay.get_by_name(context, bay_ident)
|
||||
bay_ident = bay.uuid
|
||||
|
||||
bay_uuid = bay_ident
|
||||
self.k8s_api = k8s.create_k8s_api_rc(context, bay_uuid)
|
||||
if utils.is_uuid_like(rc_ident):
|
||||
rc = objects.ReplicationController.get_by_uuid(context, rc_ident,
|
||||
bay_uuid,
|
||||
self.k8s_api)
|
||||
else:
|
||||
rc = objects.ReplicationController.get_by_name(context, rc_ident,
|
||||
bay_uuid,
|
||||
self.k8s_api)
|
||||
|
||||
return rc
|
||||
|
||||
def rc_list(self, context, bay_ident):
|
||||
# Since bay identifier is specified verify whether its a UUID
|
||||
# or Name. If name is specified as bay identifier need to extract
|
||||
# the bay uuid since its needed to get the k8s_api object.
|
||||
if not utils.is_uuid_like(bay_ident):
|
||||
bay = objects.Bay.get_by_name(context, bay_ident)
|
||||
bay_ident = bay.uuid
|
||||
|
||||
bay_uuid = bay_ident
|
||||
self.k8s_api = k8s.create_k8s_api_rc(context, bay_uuid)
|
||||
try:
|
||||
resp = self.k8s_api.list_namespaced_replication_controller(
|
||||
namespace='default')
|
||||
except rest.ApiException as err:
|
||||
raise exception.KubernetesAPIFailed(err=err)
|
||||
|
||||
if resp is None:
|
||||
raise exception.ReplicationControllerListNotFound(
|
||||
bay_uuid=bay_uuid)
|
||||
|
||||
rcs = []
|
||||
for entry in resp._items:
|
||||
rc = {}
|
||||
rc['uuid'] = entry.metadata.uid
|
||||
rc['name'] = entry.metadata.name
|
||||
rc['project_id'] = context.project_id
|
||||
rc['user_id'] = context.user_id
|
||||
rc['images'] = [
|
||||
c.image for c in entry.spec.template.spec.containers]
|
||||
rc['bay_uuid'] = bay_uuid
|
||||
# Convert string to dictionary
|
||||
rc['labels'] = ast.literal_eval(entry.metadata.labels)
|
||||
rc['replicas'] = entry.status.replicas
|
||||
|
||||
rc_obj = objects.ReplicationController(context, **rc)
|
||||
rcs.append(rc_obj)
|
||||
|
||||
return rcs
|
||||
|
|
|
@ -54,7 +54,7 @@ class K8sAPI(apiv_api.ApivApi):
|
|||
if isinstance(obj, Bay):
|
||||
bay = obj
|
||||
else:
|
||||
bay = utils.retrieve_bay(context, obj)
|
||||
bay = utils.retrieve_bay(context, obj.bay_uuid)
|
||||
if bay.magnum_cert_ref:
|
||||
self._create_certificate_files(bay)
|
||||
|
||||
|
@ -110,3 +110,87 @@ def create_k8s_api(context, obj):
|
|||
:param obj: A bay or a k8s object (Pod, Service, ReplicationController)
|
||||
"""
|
||||
return K8sAPI(context, obj)
|
||||
|
||||
|
||||
# NB : This is a place holder class. This class and create_k8s_api_rc
|
||||
# method will be removed once the objects from bay code for k8s
|
||||
# objects is merged. These changes are temporary to get the Unit
|
||||
# test working.
|
||||
class K8sAPI_RC(apiv_api.ApivApi):
|
||||
|
||||
def _create_temp_file_with_content(self, content):
|
||||
"""Creates temp file and write content to the file.
|
||||
|
||||
:param content: file content
|
||||
:returns: temp file
|
||||
"""
|
||||
try:
|
||||
tmp = NamedTemporaryFile(delete=True)
|
||||
tmp.write(content)
|
||||
tmp.flush()
|
||||
except Exception as err:
|
||||
LOG.error("Error while creating temp file: %s" % err)
|
||||
raise err
|
||||
return tmp
|
||||
|
||||
def __init__(self, context, bay_uuid):
|
||||
self.ca_file = None
|
||||
self.cert_file = None
|
||||
self.key_file = None
|
||||
|
||||
bay = utils.retrieve_bay(context, bay_uuid)
|
||||
if bay.magnum_cert_ref:
|
||||
self._create_certificate_files(bay)
|
||||
|
||||
# build a connection with Kubernetes master
|
||||
client = api_client.ApiClient(bay.api_address,
|
||||
key_file=self.key_file.name,
|
||||
cert_file=self.cert_file.name,
|
||||
ca_certs=self.ca_file.name)
|
||||
|
||||
super(K8sAPI_RC, self).__init__(client)
|
||||
|
||||
def _create_certificate_files(self, bay):
|
||||
"""Read certificate and key for a bay and stores in files.
|
||||
|
||||
:param bay: Bay object
|
||||
"""
|
||||
magnum_cert_obj = cert_manager.get_backend().CertManager.get_cert(
|
||||
bay.magnum_cert_ref)
|
||||
self.cert_file = self._create_temp_file_with_content(
|
||||
magnum_cert_obj.certificate)
|
||||
private_key = serialization.load_pem_private_key(
|
||||
magnum_cert_obj.private_key,
|
||||
password=magnum_cert_obj.private_key_passphrase,
|
||||
backend=default_backend(),
|
||||
)
|
||||
private_key = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption())
|
||||
self.key_file = self._create_temp_file_with_content(
|
||||
private_key)
|
||||
ca_cert_obj = cert_manager.get_backend().CertManager.get_cert(
|
||||
bay.ca_cert_ref)
|
||||
self.ca_file = self._create_temp_file_with_content(
|
||||
ca_cert_obj.certificate)
|
||||
|
||||
def __del__(self):
|
||||
if self.ca_file:
|
||||
self.ca_file.close()
|
||||
if self.cert_file:
|
||||
self.cert_file.close()
|
||||
if self.key_file:
|
||||
self.key_file.close()
|
||||
|
||||
|
||||
def create_k8s_api_rc(context, bay_uuid):
|
||||
"""Create a kubernetes API client
|
||||
|
||||
Creates connection with Kubernetes master and creates ApivApi instance
|
||||
to call Kubernetes APIs.
|
||||
|
||||
:param context: The security context
|
||||
:param bay_uuid: Unique identifier for the Bay
|
||||
"""
|
||||
return K8sAPI_RC(context, bay_uuid)
|
||||
|
|
|
@ -13,21 +13,21 @@
|
|||
# limitations under the License.
|
||||
|
||||
from magnum.common import clients
|
||||
from magnum import objects
|
||||
from magnum.objects import bay
|
||||
from magnum.objects import baymodel
|
||||
|
||||
|
||||
def retrieve_bay(context, obj):
|
||||
return objects.Bay.get_by_uuid(context, obj.bay_uuid)
|
||||
def retrieve_bay(context, bay_uuid):
|
||||
return bay.Bay.get_by_uuid(context, bay_uuid)
|
||||
|
||||
|
||||
def retrieve_baymodel(context, bay):
|
||||
return objects.BayModel.get_by_uuid(context, bay.baymodel_id)
|
||||
return baymodel.BayModel.get_by_uuid(context, bay.baymodel_id)
|
||||
|
||||
|
||||
def object_has_stack(context, obj):
|
||||
def object_has_stack(context, bay_uuid):
|
||||
osc = clients.OpenStackClients(context)
|
||||
if hasattr(obj, 'bay_uuid'):
|
||||
obj = retrieve_bay(context, obj)
|
||||
obj = retrieve_bay(context, bay_uuid)
|
||||
|
||||
stack = osc.heat().stacks.get(obj.stack_id)
|
||||
if (stack.stack_status == 'DELETE_COMPLETE' or
|
||||
|
|
|
@ -14,9 +14,13 @@
|
|||
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from magnum.common import exception
|
||||
from magnum.common.pythonk8sclient.swagger_client import rest
|
||||
from magnum.db import api as dbapi
|
||||
from magnum.objects import base
|
||||
|
||||
import ast
|
||||
|
||||
|
||||
@base.MagnumObjectRegistry.register
|
||||
class ReplicationController(base.MagnumPersistentObject, base.MagnumObject,
|
||||
|
@ -40,159 +44,75 @@ class ReplicationController(base.MagnumPersistentObject, base.MagnumObject,
|
|||
'manifest': fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(rc, db_rc):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in rc.fields:
|
||||
# ignore manifest_url as it was used for create rc
|
||||
if field == 'manifest_url':
|
||||
continue
|
||||
# ignore manifest as it was used for create rc
|
||||
if field == 'manifest':
|
||||
continue
|
||||
rc[field] = db_rc[field]
|
||||
|
||||
rc.obj_reset_changes()
|
||||
return rc
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object_list(db_objects, cls, context):
|
||||
"""Converts a list of database entities to a list of formal objects."""
|
||||
return [ReplicationController._from_db_object(cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, rc_id):
|
||||
"""Find a ReplicationController based on its integer id.
|
||||
|
||||
Find ReplicationController based on id and return a
|
||||
ReplicationController object.
|
||||
|
||||
:param rc_id: the id of a ReplicationController.
|
||||
:returns: a :class:`ReplicationController` object.
|
||||
"""
|
||||
db_rc = cls.dbapi.get_rc_by_id(context, rc_id)
|
||||
rc = ReplicationController._from_db_object(cls(context), db_rc)
|
||||
return rc
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
"""Find a ReplicationController based on uuid.
|
||||
|
||||
Find ReplicationController by uuid and return a
|
||||
:class:`ReplicationController` object.
|
||||
def get_by_uuid(cls, context, uuid, bay_uuid, k8s_api):
|
||||
"""Return a :class:`ReplicationController` object based on uuid.
|
||||
|
||||
:param context: Security context
|
||||
:param uuid: the uuid of a ReplicationController.
|
||||
:param context: Security context
|
||||
:param bay_uuid: the UUID of the Bay.
|
||||
|
||||
:returns: a :class:`ReplicationController` object.
|
||||
"""
|
||||
db_rc = cls.dbapi.get_rc_by_uuid(context, uuid)
|
||||
rc = ReplicationController._from_db_object(cls(context), db_rc)
|
||||
return rc
|
||||
try:
|
||||
resp = k8s_api.list_namespaced_replication_controller(
|
||||
namespace='default')
|
||||
except rest.ApiException as err:
|
||||
raise exception.KubernetesAPIFailed(err=err)
|
||||
|
||||
if resp is None:
|
||||
raise exception.ReplicationControllerListNotFound(
|
||||
bay_uuid=bay_uuid)
|
||||
|
||||
rc = {}
|
||||
for entry in resp.items:
|
||||
if entry.metadata.uid == uuid:
|
||||
rc['uuid'] = entry.metadata.uid
|
||||
rc['name'] = entry.metadata.name
|
||||
rc['project_id'] = context.project_id
|
||||
rc['user_id'] = context.user_id
|
||||
rc['images'] = [
|
||||
c.image for c in entry.spec.template.spec.containers]
|
||||
rc['bay_uuid'] = bay_uuid
|
||||
# Convert string to dictionary
|
||||
rc['labels'] = ast.literal_eval(entry.metadata.labels)
|
||||
rc['replicas'] = entry.status.replicas
|
||||
|
||||
rc_obj = ReplicationController(context, **rc)
|
||||
return rc_obj
|
||||
|
||||
raise exception.ReplicationControllerNotFound(rc=uuid)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_name(cls, context, name):
|
||||
"""Find a ReplicationController based on name.
|
||||
|
||||
Find ReplicationController by name and return a
|
||||
:class:`ReplicationController` object.
|
||||
def get_by_name(cls, context, name, bay_uuid, k8s_api):
|
||||
"""Return a :class:`ReplicationController` object based on name.
|
||||
|
||||
:param context: Security context
|
||||
:param name: the name of a ReplicationController.
|
||||
:param context: Security context
|
||||
:param bay_uuid: the UUID of the Bay.
|
||||
|
||||
:returns: a :class:`ReplicationController` object.
|
||||
"""
|
||||
db_rc = cls.dbapi.get_rc_by_name(context, name)
|
||||
rc = ReplicationController._from_db_object(cls(context), db_rc)
|
||||
return rc
|
||||
try:
|
||||
resp = k8s_api.read_namespaced_replication_controller(
|
||||
name=name,
|
||||
namespace='default')
|
||||
except rest.ApiException as err:
|
||||
raise exception.KubernetesAPIFailed(err=err)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of ReplicationController objects.
|
||||
if resp is None:
|
||||
raise exception.ReplicationControllerNotFound(rc=name)
|
||||
|
||||
:param context: Security context.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
:param marker: pagination marker for large data sets.
|
||||
:param sort_key: column to sort results by.
|
||||
:param sort_dir: direction to sort. "asc" or "desc".
|
||||
:returns: a list of :class:`ReplicationController` object.
|
||||
rc = {}
|
||||
rc['uuid'] = resp.metadata.uid
|
||||
rc['name'] = resp.metadata.name
|
||||
rc['project_id'] = context.project_id
|
||||
rc['user_id'] = context.user_id
|
||||
rc['images'] = [c.image for c in resp.spec.template.spec.containers]
|
||||
rc['bay_uuid'] = bay_uuid
|
||||
# Convert string to dictionary
|
||||
rc['labels'] = ast.literal_eval(resp.metadata.labels)
|
||||
rc['replicas'] = resp.status.replicas
|
||||
|
||||
"""
|
||||
db_rcs = cls.dbapi.get_rc_list(context, limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return ReplicationController._from_db_object_list(db_rcs, cls, context)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
"""Create a ReplicationController record in the DB.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: ReplicationController(context)
|
||||
|
||||
"""
|
||||
values = self.obj_get_changes()
|
||||
db_rc = self.dbapi.create_rc(values)
|
||||
self._from_db_object(self, db_rc)
|
||||
|
||||
@base.remotable
|
||||
def destroy(self, context=None):
|
||||
"""Delete the ReplicationController from the DB.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: ReplicationController(context)
|
||||
"""
|
||||
self.dbapi.destroy_rc(self.uuid)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def save(self, context=None):
|
||||
"""Save updates to this ReplicationController.
|
||||
|
||||
Updates will be made column by column based on the result
|
||||
of self.what_changed().
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: ReplicationController(context)
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
self.dbapi.update_rc(self.uuid, updates)
|
||||
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def refresh(self, context=None):
|
||||
"""Loads updates for this ReplicationController.
|
||||
|
||||
Loads a rc with the same uuid from the database and
|
||||
checks for updated attributes. Updates are applied from
|
||||
the loaded rc column by column, if there are any updates.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: ReplicationController(context)
|
||||
"""
|
||||
current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
|
||||
for field in self.fields:
|
||||
if field == 'manifest_url':
|
||||
continue
|
||||
if field == 'manifest':
|
||||
continue
|
||||
if self.obj_attr_is_set(field) and self[field] != current[field]:
|
||||
self[field] = current[field]
|
||||
rc_obj = ReplicationController(context, **rc)
|
||||
return rc_obj
|
||||
|
|
|
@ -149,6 +149,9 @@ extendedKeyUsage = clientAuth
|
|||
resp = self.k8s_api.delete_namespaced_replication_controller(
|
||||
name='frontend', body={}, namespace='default')
|
||||
|
||||
"""
|
||||
NB : Bug1504379. This is placeholder and will be removed when all
|
||||
the objects-from-bay patches are checked in.
|
||||
def test_pods_list(self):
|
||||
self.assertIsNotNone(self.cs.pods.list(self.bay.uuid))
|
||||
|
||||
|
@ -157,3 +160,4 @@ extendedKeyUsage = clientAuth
|
|||
|
||||
def test_services_list(self):
|
||||
self.assertIsNotNone(self.cs.services.list(self.bay.uuid))
|
||||
"""
|
||||
|
|
|
@ -15,14 +15,13 @@ import datetime
|
|||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_policy import policy
|
||||
from oslo_utils import timeutils
|
||||
from six.moves.urllib import parse as urlparse
|
||||
from wsme import types as wtypes
|
||||
|
||||
from magnum.api.controllers.v1 import replicationcontroller as api_rc
|
||||
from magnum.common.pythonk8sclient.swagger_client import rest
|
||||
from magnum.common import utils
|
||||
from magnum.conductor import api as rpcapi
|
||||
from magnum import objects
|
||||
from magnum.tests import base
|
||||
from magnum.tests.unit.api import base as api_base
|
||||
from magnum.tests.unit.api import utils as apiutils
|
||||
|
@ -43,8 +42,11 @@ class TestListRC(api_base.FunctionalTest):
|
|||
def setUp(self):
|
||||
super(TestListRC, self).setUp()
|
||||
obj_utils.create_test_bay(self.context)
|
||||
self.rc = obj_utils.create_test_rc(self.context)
|
||||
|
||||
def test_empty(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_empty(self, mock_rc_list):
|
||||
mock_rc_list.return_value = []
|
||||
response = self.get_json('/rcs')
|
||||
self.assertEqual([], response['rcs'])
|
||||
|
||||
|
@ -54,72 +56,83 @@ class TestListRC(api_base.FunctionalTest):
|
|||
for field in rc_fields:
|
||||
self.assertIn(field, rc)
|
||||
|
||||
def test_one(self):
|
||||
rc = obj_utils.create_test_rc(self.context)
|
||||
response = self.get_json('/rcs')
|
||||
self.assertEqual(rc.uuid, response['rcs'][0]["uuid"])
|
||||
self._assert_rc_fields(response['rcs'][0])
|
||||
|
||||
def test_get_one(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_get_one(self, mock_rc_show):
|
||||
rc = obj_utils.create_test_rc(self.context)
|
||||
mock_rc_show.return_value = rc
|
||||
response = self.get_json('/rcs/%s/%s' % (rc['uuid'], rc['bay_uuid']))
|
||||
self.assertEqual(rc.uuid, response['uuid'])
|
||||
self._assert_rc_fields(response)
|
||||
|
||||
def test_get_one_by_name(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_get_one_by_name(self, mock_rc_show):
|
||||
rc = obj_utils.create_test_rc(self.context)
|
||||
mock_rc_show.return_value = rc
|
||||
response = self.get_json('/rcs/%s/%s' % (rc['name'], rc['bay_uuid']))
|
||||
self.assertEqual(rc.uuid, response['uuid'])
|
||||
self._assert_rc_fields(response)
|
||||
|
||||
def test_get_one_by_name_not_found(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_get_one_by_name_not_found(self, mock_rc_show):
|
||||
err = rest.ApiException(status=404)
|
||||
mock_rc_show.side_effect = err
|
||||
response = self.get_json(
|
||||
'/rcs/not_found/5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_get_one_by_name_multiple_rc(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_get_one_by_name_multiple_rc(self, mock_rc_show):
|
||||
obj_utils.create_test_rc(
|
||||
self.context, name='test_rc',
|
||||
uuid=utils.generate_uuid())
|
||||
obj_utils.create_test_rc(
|
||||
self.context, name='test_rc',
|
||||
uuid=utils.generate_uuid())
|
||||
err = rest.ApiException(status=500)
|
||||
mock_rc_show.side_effect = err
|
||||
response = self.get_json(
|
||||
'/rcs/test_rc/5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
|
||||
expect_errors=True)
|
||||
self.assertEqual(409, response.status_int)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_get_all_with_pagination_marker(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_get_all_with_pagination_marker(self, mock_rc_list):
|
||||
rc_list = []
|
||||
for id_ in range(4):
|
||||
rc = obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
rc_list.append(rc.uuid)
|
||||
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs?limit=3&marker=%s' % rc_list[2])
|
||||
self.assertEqual(1, len(response['rcs']))
|
||||
self.assertEqual(rc_list[-1], response['rcs'][0]['uuid'])
|
||||
|
||||
def test_detail(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_detail(self, mock_rc_list):
|
||||
rc = obj_utils.create_test_rc(self.context)
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs/detail')
|
||||
self.assertEqual(rc.uuid, response['rcs'][0]["uuid"])
|
||||
self._assert_rc_fields(response['rcs'][0])
|
||||
|
||||
def test_detail_with_pagination_marker(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_detail_with_pagination_marker(self, mock_rc_list):
|
||||
rc_list = []
|
||||
for id_ in range(4):
|
||||
rc = obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
rc_list.append(rc.uuid)
|
||||
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs/detail?limit=3&marker=%s'
|
||||
% rc_list[2])
|
||||
% (rc_list[2]))
|
||||
|
||||
self.assertEqual(1, len(response['rcs']))
|
||||
self.assertEqual(rc_list[-1], response['rcs'][0]['uuid'])
|
||||
self._assert_rc_fields(response['rcs'][0])
|
||||
|
@ -130,46 +143,47 @@ class TestListRC(api_base.FunctionalTest):
|
|||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_many(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_many(self, mock_rc_list):
|
||||
rc_list = []
|
||||
for id_ in range(5):
|
||||
for id_ in range(1):
|
||||
rc = obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
rc_list.append(rc.uuid)
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs')
|
||||
self.assertEqual(len(rc_list), len(response['rcs']))
|
||||
uuids = [r['uuid'] for r in response['rcs']]
|
||||
self.assertEqual(sorted(rc_list), sorted(uuids))
|
||||
|
||||
def test_links(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_links(self, mock_rc_show):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_test_rc(self.context, id=1, uuid=uuid)
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (uuid, '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'))
|
||||
rc = obj_utils.create_test_rc(self.context, id=1, uuid=uuid)
|
||||
mock_rc_show.return_value = rc
|
||||
response = self.get_json('/rcs/%s/%s' % (uuid, rc.bay_uuid))
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
self.assertIn(uuid, response['links'][0]['href'])
|
||||
|
||||
def test_collection_links(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_collection_links(self, mock_rc_list):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
response = self.get_json('/rcs/?limit=3')
|
||||
self.assertEqual(3, len(response['rcs']))
|
||||
rc = obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs/?limit=1')
|
||||
self.assertEqual(1, len(response['rcs']))
|
||||
|
||||
next_marker = response['rcs'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_collection_links_default_limit(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_list')
|
||||
def test_collection_links_default_limit(self, mock_rc_list):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api')
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
rc = obj_utils.create_test_rc(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
mock_rc_list.return_value = [rc]
|
||||
response = self.get_json('/rcs')
|
||||
self.assertEqual(3, len(response['rcs']))
|
||||
|
||||
next_marker = response['rcs'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
self.assertEqual(1, len(response['rcs']))
|
||||
|
||||
|
||||
class TestPatch(api_base.FunctionalTest):
|
||||
|
@ -182,49 +196,58 @@ class TestPatch(api_base.FunctionalTest):
|
|||
self.another_bay = obj_utils.create_test_bay(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_image = 'rc_example_B_image'
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid))
|
||||
self.assertNotEqual(new_image, response['images'][0])
|
||||
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/images/0',
|
||||
'value': new_image,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid))
|
||||
self.assertEqual(new_image, response['images'][0])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
self.manifest = '''{
|
||||
"metadata": {
|
||||
"name": "name_of_rc"
|
||||
},
|
||||
"spec":{
|
||||
"replicas":2,
|
||||
"selector":{
|
||||
"name":"frontend"
|
||||
},
|
||||
"template":{
|
||||
"metadata":{
|
||||
"labels":{
|
||||
"name":"frontend"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"containers":[
|
||||
{
|
||||
"name":"test-redis",
|
||||
"image":"steak/for-dinner",
|
||||
"ports":[
|
||||
{
|
||||
"containerPort":80,
|
||||
"protocol":"TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}'''
|
||||
|
||||
def test_replace_bay_uuid(self):
|
||||
response = self.patch_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
[{'path': '/bay_uuid',
|
||||
'value': self.another_bay.uuid,
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.rc.manifest = '{"bay_uuid": "self.rc.bay_uuid"}'
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/bay_uuid',
|
||||
'value': self.another_bay.uuid,
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_replace_non_existent_bay_uuid(self):
|
||||
response = self.patch_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
[{'path': '/bay_uuid',
|
||||
'value': utils.generate_uuid(),
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.rc.manifest = '{"key": "value"}'
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/bay_uuid',
|
||||
'value': utils.generate_uuid(),
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
@ -246,18 +269,19 @@ class TestPatch(api_base.FunctionalTest):
|
|||
'value': 'rc_example_B_image',
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
@mock.patch.object(rpcapi.API, 'rc_update')
|
||||
@mock.patch.object(api_rc.ReplicationController, 'parse_manifest')
|
||||
def test_replace_with_manifest(self, parse_manifest, rc_update):
|
||||
response = self.patch_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
[{'path': '/manifest',
|
||||
'value': '{}',
|
||||
'op': 'replace'}])
|
||||
rc_update.return_value = self.rc
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/manifest',
|
||||
'value': '{"foo": "bar"}',
|
||||
'op': 'replace'}])
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
parse_manifest.assert_called_once_with()
|
||||
|
@ -272,26 +296,32 @@ class TestPatch(api_base.FunctionalTest):
|
|||
self.assertEqual(400, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_ok(self):
|
||||
response = self.get_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid))
|
||||
@mock.patch.object(rpcapi.API, 'rc_update')
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_remove_ok(self, mock_rc_show, mock_rc_update):
|
||||
mock_rc_show.return_value = self.rc
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid))
|
||||
self.assertNotEqual(len(response['images']), 0)
|
||||
|
||||
response = self.patch_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
[{'path': '/images', 'op': 'remove'}])
|
||||
mock_rc_update.return_value = self.rc
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/manifest',
|
||||
'op': 'remove'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid))
|
||||
self.assertEqual(0, len(response['images']))
|
||||
mock_rc_show.return_value = self.rc
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid))
|
||||
self.assertEqual(len(response['images']), 1)
|
||||
|
||||
def test_remove_uuid(self):
|
||||
response = self.patch_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
@ -323,25 +353,30 @@ class TestPatch(api_base.FunctionalTest):
|
|||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok_by_name(self, mock_utcnow):
|
||||
new_image = 'rc_example_B_image'
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
@mock.patch.object(rpcapi.API, 'rc_update')
|
||||
@mock.patch.object(api_rc.ReplicationController, 'parse_manifest')
|
||||
def test_replace_ok_by_name(self, parse_manifest,
|
||||
mock_rc_update,
|
||||
mock_rc_show):
|
||||
mock_rc_update.return_value = self.rc
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (self.rc.name, self.rc.bay_uuid),
|
||||
[{'path': '/images/0',
|
||||
'value': new_image,
|
||||
[{'path': '/manifest',
|
||||
'value': '{"foo": "bar"}',
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
parse_manifest.assert_called_once_with()
|
||||
self.assertTrue(mock_rc_update.is_called)
|
||||
|
||||
response = self.get_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid))
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
mock_rc_show.return_value = self.rc
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok_by_name_not_found(self, mock_utcnow):
|
||||
|
@ -350,13 +385,14 @@ class TestPatch(api_base.FunctionalTest):
|
|||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
response = self.patch_json('/rcs/%s/%s' % (name, self.rc.bay_uuid),
|
||||
[{'path': '/images/0',
|
||||
'value': new_image,
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
response = self.patch_json(
|
||||
'/rcs/%s/%s' % (name, self.rc.bay_uuid),
|
||||
[{'path': '/images/0',
|
||||
'value': new_image,
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(404, response.status_code)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok_by_name_multiple_rc(self, mock_utcnow):
|
||||
|
@ -376,7 +412,7 @@ class TestPatch(api_base.FunctionalTest):
|
|||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(409, response.status_code)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
|
||||
class TestPost(api_base.FunctionalTest):
|
||||
|
@ -384,25 +420,24 @@ class TestPost(api_base.FunctionalTest):
|
|||
def setUp(self):
|
||||
super(TestPost, self).setUp()
|
||||
obj_utils.create_test_bay(self.context)
|
||||
self.rc_obj = obj_utils.create_test_rc(self.context)
|
||||
p = mock.patch.object(rpcapi.API, 'rc_create')
|
||||
self.mock_rc_create = p.start()
|
||||
self.mock_rc_create.side_effect = self._simulate_rpc_rc_create
|
||||
self.mock_rc_create.return_value = self.rc_obj
|
||||
self.addCleanup(p.stop)
|
||||
p = mock.patch('magnum.objects.BayModel.get_by_uuid')
|
||||
self.mock_baymodel_get_by_uuid = p.start()
|
||||
self.mock_baymodel_get_by_uuid.return_value.coe = 'kubernetes'
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_rc_create(self, rc):
|
||||
rc.create(self.context)
|
||||
return rc
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_create_rc(self, mock_utcnow):
|
||||
@mock.patch.object(rpcapi.API, 'rc_create')
|
||||
def test_create_rc(self, mock_rc_create, mock_utcnow):
|
||||
rc_dict = apiutils.rc_post_data()
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
mock_rc_create.return_value = self.rc_obj
|
||||
response = self.post_json('/rcs', rc_dict)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
|
@ -412,38 +447,24 @@ class TestPost(api_base.FunctionalTest):
|
|||
self.assertEqual(expected_location,
|
||||
urlparse.urlparse(response.location).path)
|
||||
self.assertEqual(rc_dict['uuid'], response.json['uuid'])
|
||||
self.assertNotIn('updated_at', response.json.keys)
|
||||
return_created_at = timeutils.parse_isotime(
|
||||
response.json['created_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_created_at)
|
||||
|
||||
def test_create_rc_set_project_id_and_user_id(self):
|
||||
rc_dict = apiutils.rc_post_data()
|
||||
|
||||
def _simulate_rpc_rc_create(rc):
|
||||
self.assertEqual(self.context.project_id, rc.project_id)
|
||||
self.assertEqual(self.context.user_id, rc.user_id)
|
||||
rc.create()
|
||||
self.assertEqual(rc.project_id, self.context.project_id)
|
||||
self.assertEqual(rc.user_id, self.context.user_id)
|
||||
return rc
|
||||
self.mock_rc_create.side_effect = _simulate_rpc_rc_create
|
||||
|
||||
self.post_json('/rcs', rc_dict)
|
||||
|
||||
def test_create_rc_doesnt_contain_id(self):
|
||||
with mock.patch.object(self.dbapi, 'create_rc',
|
||||
wraps=self.dbapi.create_rc) as cc_mock:
|
||||
rc_dict = apiutils.rc_post_data()
|
||||
response = self.post_json('/rcs', rc_dict)
|
||||
self.assertEqual(rc_dict['images'], response.json['images'])
|
||||
cc_mock.assert_called_once_with(mock.ANY)
|
||||
# Check that 'id' is not in first arg of positional args
|
||||
self.assertNotIn('id', cc_mock.call_args[0][0])
|
||||
|
||||
def test_create_rc_generate_uuid(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_create')
|
||||
def test_create_rc_generate_uuid(self, mock_rc_create):
|
||||
rc_dict = apiutils.rc_post_data()
|
||||
del rc_dict['uuid']
|
||||
|
||||
response = self.post_json('/rcs', rc_dict)
|
||||
mock_rc_create.return_value = self.rc_obj
|
||||
response = self.post_json('/rcs', rc_dict, expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual(rc_dict['images'], response.json['images'])
|
||||
|
@ -494,54 +515,60 @@ class TestDelete(api_base.FunctionalTest):
|
|||
super(TestDelete, self).setUp()
|
||||
obj_utils.create_test_bay(self.context)
|
||||
self.rc = obj_utils.create_test_rc(self.context)
|
||||
p = mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
self.mock_rc_delete = p.start()
|
||||
self.mock_rc_delete.side_effect = self._simulate_rpc_rc_delete
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_rc_delete(self, rc_uuid):
|
||||
rc = objects.ReplicationController.get_by_uuid(self.context, rc_uuid)
|
||||
rc.destroy()
|
||||
|
||||
def test_delete_rc(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
@mock.patch.object(rpcapi.API, 'rc_show')
|
||||
def test_delete_rc(self, mock_rc_show, mock_rc_delete):
|
||||
self.delete('/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid))
|
||||
response = self.get_json('/rcs/%s/%s' % (self.rc.uuid,
|
||||
self.rc.bay_uuid),
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
err = rest.ApiException(status=404)
|
||||
mock_rc_show.side_effect = err
|
||||
response = self.get_json(
|
||||
'/rcs/%s/%s' % (self.rc.uuid, self.rc.bay_uuid),
|
||||
expect_errors=True)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_delete_rc_not_found(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
def test_delete_rc_not_found(self, mock_rc_delete):
|
||||
uuid = utils.generate_uuid()
|
||||
err = rest.ApiException(status=404)
|
||||
mock_rc_delete.side_effect = err
|
||||
response = self.delete('/rcs/%s/%s' % (uuid, self.rc.bay_uuid),
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_delete_rc_with_name_not_found(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
def test_delete_rc_with_name_not_found(self, mock_rc_delete):
|
||||
err = rest.ApiException(status=404)
|
||||
mock_rc_delete.side_effect = err
|
||||
response = self.delete(
|
||||
'/rcs/not_found/5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_delete_rc_with_name(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
def test_delete_rc_with_name(self, mock_rc_delete):
|
||||
response = self.delete('/rcs/%s/%s' % (self.rc.name, self.rc.bay_uuid),
|
||||
expect_errors=True)
|
||||
self.assertEqual(204, response.status_int)
|
||||
|
||||
def test_delete_multiple_rc_by_name(self):
|
||||
@mock.patch.object(rpcapi.API, 'rc_delete')
|
||||
def test_delete_multiple_rc_by_name(self, mock_rc_delete):
|
||||
err = rest.ApiException(status=409)
|
||||
mock_rc_delete.side_effect = err
|
||||
obj_utils.create_test_rc(self.context, name='test_rc',
|
||||
uuid=utils.generate_uuid())
|
||||
obj_utils.create_test_rc(self.context, name='test_rc',
|
||||
uuid=utils.generate_uuid())
|
||||
response = self.delete(
|
||||
'/rcs/test_rc/5d12f6fd-a196-4bf0-ae4c-1f639a523a5',
|
||||
'/rcs/test_rc/5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
|
||||
expect_errors=True)
|
||||
self.assertEqual(409, response.status_int)
|
||||
self.assertEqual(500, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
|
|
@ -125,9 +125,11 @@ class TestK8sConductor(base.TestCase):
|
|||
self.assertRaises(exception.KubernetesAPIFailed,
|
||||
self.kube_handler.pod_delete,
|
||||
self.context, mock_pod.uuid)
|
||||
(mock_kube_api.return_value.delete_namespaced_pod
|
||||
.assert_called_once_with(
|
||||
name=mock_pod.name, body={}, namespace='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')
|
||||
|
@ -253,25 +255,30 @@ class TestK8sConductor(base.TestCase):
|
|||
name=mock_service.name, namespace='default'))
|
||||
mock_service.destroy.assert_called_once_with(self.context)
|
||||
|
||||
def test_rc_create_with_success(self):
|
||||
expected_rc = self.mock_rc()
|
||||
expected_rc.create = mock.MagicMock()
|
||||
@patch('ast.literal_eval')
|
||||
def test_rc_create_with_success(self, mock_ast):
|
||||
expected_rc = mock.MagicMock()
|
||||
manifest = {"key": "value"}
|
||||
expected_rc.name = 'test-name'
|
||||
expected_rc.uuid = 'test-uuid'
|
||||
expected_rc.bay_uuid = 'test-bay-uuid'
|
||||
expected_rc.manifest = '{"key": "value"}'
|
||||
mock_ast.return_value = {}
|
||||
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api_rc') as \
|
||||
mock_kube_api:
|
||||
self.kube_handler.rc_create({}, expected_rc)
|
||||
(mock_kube_api.return_value
|
||||
.create_namespaced_replication_controller
|
||||
.assert_called_once_with(body=manifest, namespace='default'))
|
||||
|
||||
def test_rc_create_with_failure(self):
|
||||
expected_rc = self.mock_rc()
|
||||
expected_rc.create = mock.MagicMock()
|
||||
expected_rc = mock.MagicMock()
|
||||
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_rc') as \
|
||||
mock_kube_api:
|
||||
err = rest.ApiException(status=500)
|
||||
(mock_kube_api.return_value
|
||||
.create_namespaced_replication_controller.side_effect) = err
|
||||
|
@ -282,46 +289,57 @@ class TestK8sConductor(base.TestCase):
|
|||
(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')
|
||||
@patch('magnum.objects.ReplicationController.get_by_uuid')
|
||||
def test_rc_delete_with_success(self,
|
||||
mock_rc_get_by_uuid,
|
||||
@patch('magnum.objects.ReplicationController.get_by_name')
|
||||
@patch('magnum.objects.Bay.get_by_name')
|
||||
def test_rc_delete_with_success(self, mock_bay_get_by_name,
|
||||
mock_rc_get_by_name,
|
||||
mock_object_has_stack):
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay_get_by_name.return_value = mock_bay
|
||||
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.name = 'test-rc'
|
||||
mock_rc.uuid = 'test-uuid'
|
||||
mock_rc_get_by_uuid.return_value = mock_rc
|
||||
mock_rc_get_by_name.return_value = mock_rc
|
||||
bay_uuid = 'test-bay-uuid'
|
||||
|
||||
mock_object_has_stack.return_value = True
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
|
||||
self.kube_handler.rc_delete(self.context, mock_rc.uuid)
|
||||
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api_rc') as \
|
||||
mock_kube_api:
|
||||
self.kube_handler.rc_delete(self.context, mock_rc.name, bay_uuid)
|
||||
(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')
|
||||
def test_rc_delete_with_failure(self, mock_rc_get_by_uuid,
|
||||
@patch('magnum.objects.Bay.get_by_name')
|
||||
def test_rc_delete_with_failure(self, mock_bay_get_by_name,
|
||||
mock_rc_get_by_uuid,
|
||||
mock_object_has_stack):
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay_get_by_name.return_value = mock_bay
|
||||
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.name = 'test-rc'
|
||||
mock_rc.uuid = 'test-uuid'
|
||||
mock_rc.bay_uuid = 'test-bay-uuid'
|
||||
mock_rc_get_by_uuid.return_value = mock_rc
|
||||
|
||||
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_rc') as \
|
||||
mock_kube_api:
|
||||
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)
|
||||
self.context, mock_rc.name,
|
||||
mock_rc.bay_uuid)
|
||||
|
||||
(mock_kube_api.return_value
|
||||
.delete_namespaced_replication_controller
|
||||
|
@ -331,56 +349,88 @@ class TestK8sConductor(base.TestCase):
|
|||
|
||||
@patch('magnum.conductor.utils.object_has_stack')
|
||||
@patch('magnum.objects.ReplicationController.get_by_uuid')
|
||||
@patch('magnum.objects.Bay.get_by_name')
|
||||
def test_rc_delete_succeeds_when_not_found(
|
||||
self,
|
||||
self, mock_bay_get_by_name,
|
||||
mock_rc_get_by_uuid,
|
||||
mock_object_has_stack):
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay_get_by_name.return_value = mock_bay
|
||||
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.name = 'test-rc'
|
||||
mock_rc.uuid = 'test-uuid'
|
||||
mock_rc.bay_uuid = 'test-bay-uuid'
|
||||
mock_rc_get_by_uuid.return_value = mock_rc
|
||||
|
||||
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_rc') as \
|
||||
mock_kube_api:
|
||||
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)
|
||||
self.kube_handler.rc_delete(self.context,
|
||||
mock_rc.name,
|
||||
mock_rc.bay_uuid)
|
||||
|
||||
(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):
|
||||
expected_rc = self.mock_rc()
|
||||
@patch('magnum.objects.ReplicationController.get_by_name')
|
||||
@patch('magnum.objects.ReplicationController.get_by_uuid')
|
||||
@patch('magnum.objects.Bay.get_by_name')
|
||||
@patch('ast.literal_eval')
|
||||
def test_rc_update_with_success(self, mock_ast,
|
||||
mock_bay_get_by_name,
|
||||
mock_rc_get_by_uuid,
|
||||
mock_rc_get_by_name):
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay_get_by_name.return_value = mock_bay
|
||||
|
||||
expected_rc = mock.MagicMock()
|
||||
expected_rc.uuid = 'test-uuid'
|
||||
expected_rc.name = 'test-name'
|
||||
expected_rc.refresh = mock.MagicMock()
|
||||
expected_rc.save = mock.MagicMock()
|
||||
manifest = {"key": "value"}
|
||||
expected_rc.bay_uuid = 'test-bay-uuid'
|
||||
expected_rc.manifest = '{"key": "value"}'
|
||||
mock_ast.return_value = {}
|
||||
mock_rc_get_by_uuid.return_value = expected_rc
|
||||
mock_rc_get_by_name.return_value = expected_rc
|
||||
name_rc = expected_rc.name
|
||||
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
|
||||
self.kube_handler.rc_update(self.context, expected_rc)
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api_rc') as \
|
||||
mock_kube_api:
|
||||
self.kube_handler.rc_update(self.context, expected_rc.name,
|
||||
expected_rc.bay_uuid,
|
||||
expected_rc.manifest)
|
||||
(mock_kube_api.return_value
|
||||
.replace_namespaced_replication_controller
|
||||
.assert_called_once_with(body=manifest, name=expected_rc.name,
|
||||
namespace='default'))
|
||||
expected_rc.refresh.assert_called_once_with(self.context)
|
||||
expected_rc.save.assert_called_once_with()
|
||||
.replace_namespaced_replication_controller
|
||||
.assert_called_once_with(body=expected_rc.manifest,
|
||||
name=name_rc,
|
||||
namespace='default'))
|
||||
|
||||
def test_rc_update_with_failure(self):
|
||||
expected_rc = self.mock_rc()
|
||||
@patch('magnum.objects.ReplicationController.get_by_name')
|
||||
@patch('magnum.objects.ReplicationController.get_by_uuid')
|
||||
@patch('magnum.objects.Bay.get_by_name')
|
||||
def test_rc_update_with_failure(self, mock_bay_get_by_name,
|
||||
mock_rc_get_by_uuid,
|
||||
mock_rc_get_by_name):
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay_get_by_name.return_value = mock_bay
|
||||
|
||||
expected_rc = mock.MagicMock()
|
||||
expected_rc.uuid = 'test-uuid'
|
||||
expected_rc.name = 'test-name'
|
||||
expected_rc.update = mock.MagicMock()
|
||||
manifest = {"key": "value"}
|
||||
expected_rc.bay_uuid = 'test-bay-uuid'
|
||||
mock_rc_get_by_uuid.return_value = expected_rc
|
||||
mock_rc_get_by_name.return_value = expected_rc
|
||||
expected_rc.manifest = '{"key": "value"}'
|
||||
name_rc = expected_rc.name
|
||||
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api') as mock_kube_api:
|
||||
with patch('magnum.conductor.k8s_api.create_k8s_api_rc') as \
|
||||
mock_kube_api:
|
||||
err = rest.ApiException(status=404)
|
||||
(mock_kube_api.return_value
|
||||
.replace_namespaced_replication_controller
|
||||
|
@ -388,12 +438,14 @@ class TestK8sConductor(base.TestCase):
|
|||
|
||||
self.assertRaises(exception.KubernetesAPIFailed,
|
||||
self.kube_handler.rc_update,
|
||||
self.context, expected_rc)
|
||||
self.context, expected_rc.name,
|
||||
expected_rc.bay_uuid,
|
||||
expected_rc.manifest)
|
||||
(mock_kube_api.return_value
|
||||
.replace_namespaced_replication_controller
|
||||
.assert_called_once_with(body=manifest, name=expected_rc.name,
|
||||
.assert_called_once_with(body=expected_rc.manifest,
|
||||
name=name_rc,
|
||||
namespace='default'))
|
||||
self.assertFalse(expected_rc.update.called)
|
||||
|
||||
def test_service_update_with_success(self):
|
||||
expected_service = self.mock_service()
|
||||
|
|
|
@ -102,7 +102,7 @@ class TestK8sAPI(base.TestCase):
|
|||
k8s_api.create_k8s_api(context, obj)
|
||||
|
||||
if cls is not 'Bay':
|
||||
mock_bay_retrieval.assert_called_once_with(context, obj)
|
||||
mock_bay_retrieval.assert_called_once_with(context, obj.bay_uuid)
|
||||
|
||||
mock_api_client.assert_called_once_with(
|
||||
bay_obj.api_address,
|
||||
|
|
|
@ -157,18 +157,22 @@ class RPCAPITestCase(base.DbTestCase):
|
|||
self._test_rpcapi('rc_update',
|
||||
'call',
|
||||
version='1.0',
|
||||
rc=self.fake_rc)
|
||||
rc_ident=self.fake_rc['uuid'],
|
||||
bay_ident=self.fake_rc['bay_uuid'],
|
||||
manifest={})
|
||||
|
||||
def test_rc_delete(self):
|
||||
self._test_rpcapi('rc_delete',
|
||||
'call',
|
||||
version='1.0',
|
||||
uuid=self.fake_rc['uuid'])
|
||||
rc_ident=self.fake_rc['uuid'],
|
||||
bay_ident=self.fake_rc['bay_uuid'])
|
||||
|
||||
self._test_rpcapi('rc_delete',
|
||||
'call',
|
||||
version='1.1',
|
||||
uuid=self.fake_rc['name'])
|
||||
rc_ident=self.fake_rc['uuid'],
|
||||
bay_ident=self.fake_rc['bay_uuid'])
|
||||
|
||||
def test_container_create(self):
|
||||
self._test_rpcapi('container_create',
|
||||
|
|
|
@ -21,35 +21,40 @@ from magnum.tests import base
|
|||
|
||||
class TestConductorUtils(base.TestCase):
|
||||
|
||||
def _test_retrieve_bay(self, obj, mock_bay_get_by_uuid):
|
||||
def _test_retrieve_bay(self, expected_bay_uuid, mock_bay_get_by_uuid):
|
||||
expected_context = 'context'
|
||||
expected_bay_uuid = 'bay_uuid'
|
||||
|
||||
obj.bay_uuid = expected_bay_uuid
|
||||
utils.retrieve_bay(expected_context, obj)
|
||||
utils.retrieve_bay(expected_context, expected_bay_uuid)
|
||||
mock_bay_get_by_uuid.assert_called_once_with(expected_context,
|
||||
expected_bay_uuid)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_retrieve_bay_from_pod(self,
|
||||
mock_bay_get_by_uuid):
|
||||
self._test_retrieve_bay(objects.Pod({}), mock_bay_get_by_uuid)
|
||||
pod = objects.Pod({})
|
||||
pod.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
|
||||
self._test_retrieve_bay(pod.bay_uuid, mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_retrieve_bay_from_service(self,
|
||||
mock_bay_get_by_uuid):
|
||||
self._test_retrieve_bay(objects.Service({}), mock_bay_get_by_uuid)
|
||||
service = objects.Service({})
|
||||
service.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
|
||||
self._test_retrieve_bay(service.bay_uuid, mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_retrieve_bay_from_rc(self,
|
||||
mock_bay_get_by_uuid):
|
||||
self._test_retrieve_bay(objects.ReplicationController({}),
|
||||
rc = objects.ReplicationController({})
|
||||
rc.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
|
||||
self._test_retrieve_bay(rc.bay_uuid,
|
||||
mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_retrieve_bay_from_container(self,
|
||||
mock_bay_get_by_uuid):
|
||||
self._test_retrieve_bay(objects.Container({}), mock_bay_get_by_uuid)
|
||||
container = objects.Container({})
|
||||
container.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
|
||||
self._test_retrieve_bay(container.bay_uuid, mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_retrieve_baymodel(self, mock_baymodel_get_by_uuid):
|
||||
|
|
|
@ -431,7 +431,7 @@ object_data = {
|
|||
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',
|
||||
'Node': '1.0-30943e6e3387a2fae7490b57c4239a17',
|
||||
'Pod': '1.1-7a31c372f163742845c10a008f47cc15',
|
||||
'ReplicationController': '1.0-782b7deb9307b2807101541b7e58b8a2',
|
||||
'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab',
|
||||
'Service': '1.0-a8cf7e95fced904419164dbcb6d32b38',
|
||||
'X509KeyPair': '1.1-4aecc268e23e32b8a762d43ba1a4b159',
|
||||
'MagnumService': '1.0-2d397ec59b0046bd5ec35cd3e06efeca',
|
||||
|
|
|
@ -13,14 +13,12 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from magnum.common import utils as magnum_utils
|
||||
from magnum import objects
|
||||
from magnum.tests.unit.db import base
|
||||
from magnum.tests.unit.db import utils
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class TestReplicationControllerObject(base.DbTestCase):
|
||||
|
||||
|
@ -28,99 +26,70 @@ class TestReplicationControllerObject(base.DbTestCase):
|
|||
super(TestReplicationControllerObject, self).setUp()
|
||||
self.fake_rc = utils.get_test_rc()
|
||||
|
||||
def test_get_by_id(self):
|
||||
rc_id = self.fake_rc['id']
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_id',
|
||||
autospec=True) as mock_get_rc:
|
||||
mock_get_rc.return_value = self.fake_rc
|
||||
rc = objects.ReplicationController.get_by_id(self.context,
|
||||
rc_id)
|
||||
mock_get_rc.assert_called_once_with(self.context, rc_id)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
@mock.patch('magnum.conductor.k8s_api.create_k8s_api')
|
||||
@mock.patch('ast.literal_eval')
|
||||
def test_get_by_uuid(self, mock_ast, mock_kube_api):
|
||||
uuid = self.fake_rc['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_uuid',
|
||||
autospec=True) as mock_get_rc:
|
||||
mock_get_rc.return_value = self.fake_rc
|
||||
rc = objects.ReplicationController.get_by_uuid(self.context,
|
||||
uuid)
|
||||
mock_get_rc.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
bay_uuid = self.fake_rc['bay_uuid']
|
||||
mock_ast.return_value = {}
|
||||
k8s_api_mock = mock.MagicMock()
|
||||
mock_kube_api.return_value = k8s_api_mock
|
||||
|
||||
def test_get_by_name(self):
|
||||
fake_obj = mock.MagicMock()
|
||||
items = [
|
||||
{
|
||||
'metadata': {
|
||||
'uid': '10a47dd1-4874-4298-91cf-eff046dbdb8d',
|
||||
'name': 'fake-name',
|
||||
'labels': {}
|
||||
},
|
||||
'status': {'replicas': 10},
|
||||
'spec': {
|
||||
'template': {
|
||||
'spec': {
|
||||
'containers': [
|
||||
{
|
||||
'image': 'fake-images'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
fake_obj.items = items
|
||||
fake_obj.items[0] = mock.MagicMock()
|
||||
fake_obj.items[0].metadata = mock.MagicMock()
|
||||
fake_obj.items[0].metadata.uid = '10a47dd1-4874-4298-91cf-eff046dbdb8d'
|
||||
fake_obj.items[0].metadata.name = 'fake-name'
|
||||
k8s_api_mock.list_namespaced_replication_controller\
|
||||
.return_value = fake_obj
|
||||
objects.ReplicationController.get_by_uuid(self.context,
|
||||
uuid,
|
||||
bay_uuid,
|
||||
k8s_api_mock)
|
||||
(k8s_api_mock.list_namespaced_replication_controller
|
||||
.assert_called_once_with(namespace='default'))
|
||||
|
||||
@mock.patch('magnum.conductor.k8s_api.create_k8s_api')
|
||||
@mock.patch('ast.literal_eval')
|
||||
def test_get_by_name(self, mock_ast, mock_kube_api):
|
||||
name = self.fake_rc['name']
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_name',
|
||||
autospec=True) as mock_get_rc:
|
||||
mock_get_rc.return_value = self.fake_rc
|
||||
rc = objects.ReplicationController.get_by_name(self.context,
|
||||
name)
|
||||
mock_get_rc.assert_called_once_with(self.context, name)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_rc_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_rc]
|
||||
rcs = objects.ReplicationController.list(self.context)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertThat(rcs, HasLength(1))
|
||||
self.assertIsInstance(rcs[0], objects.ReplicationController)
|
||||
self.assertEqual(self.context, rcs[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_rc',
|
||||
autospec=True) as mock_create_rc:
|
||||
mock_create_rc.return_value = self.fake_rc
|
||||
rc = objects.ReplicationController(self.context, **self.fake_rc)
|
||||
rc.create()
|
||||
mock_create_rc.assert_called_once_with(self.fake_rc)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_rc['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_uuid',
|
||||
autospec=True) as mock_get_rc:
|
||||
mock_get_rc.return_value = self.fake_rc
|
||||
with mock.patch.object(self.dbapi, 'destroy_rc',
|
||||
autospec=True) as mock_destroy_rc:
|
||||
rc = objects.ReplicationController.get_by_uuid(self.context,
|
||||
uuid)
|
||||
rc.destroy()
|
||||
mock_get_rc.assert_called_once_with(self.context, uuid)
|
||||
mock_destroy_rc.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_rc['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_uuid',
|
||||
autospec=True) as mock_get_rc:
|
||||
mock_get_rc.return_value = self.fake_rc
|
||||
with mock.patch.object(self.dbapi, 'update_rc',
|
||||
autospec=True) as mock_update_rc:
|
||||
rc = objects.ReplicationController.get_by_uuid(self.context,
|
||||
uuid)
|
||||
rc.replicas = 10
|
||||
rc.save()
|
||||
|
||||
mock_get_rc.assert_called_once_with(self.context, uuid)
|
||||
mock_update_rc.assert_called_once_with(
|
||||
uuid, {'replicas': 10})
|
||||
self.assertEqual(self.context, rc._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_rc['uuid']
|
||||
new_uuid = magnum_utils.generate_uuid()
|
||||
returns = [dict(self.fake_rc, uuid=uuid),
|
||||
dict(self.fake_rc, uuid=new_uuid)]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_rc_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_rc:
|
||||
rc = objects.ReplicationController.get_by_uuid(self.context, uuid)
|
||||
self.assertEqual(uuid, rc.uuid)
|
||||
rc.refresh()
|
||||
self.assertEqual(new_uuid, rc.uuid)
|
||||
self.assertEqual(expected, mock_get_rc.call_args_list)
|
||||
self.assertEqual(self.context, rc._context)
|
||||
bay_uuid = self.fake_rc['bay_uuid']
|
||||
mock_ast.return_value = {}
|
||||
k8s_api_mock = mock.MagicMock()
|
||||
mock_kube_api.return_value = k8s_api_mock
|
||||
fake_rc = mock.MagicMock()
|
||||
fake_rc.metadata.uid = 'fake-uuid'
|
||||
fake_rc.metadata.name = 'fake-name'
|
||||
fake_rc.items[0].spec.template.spec.containers.image = ['fake-images']
|
||||
fake_rc.metadata.labels = mock_ast.return_value
|
||||
fake_rc.status.replicas = 10
|
||||
k8s_api_mock.read_namespaced_replication_controller\
|
||||
.return_value = fake_rc
|
||||
objects.ReplicationController.get_by_name(self.context,
|
||||
name,
|
||||
bay_uuid,
|
||||
k8s_api_mock)
|
||||
(k8s_api_mock.read_namespaced_replication_controller
|
||||
.assert_called_once_with(name=name, namespace='default'))
|
||||
|
|
|
@ -149,7 +149,7 @@ def create_test_rc(context, **kw):
|
|||
ReplicationController object with appropriate attributes.
|
||||
"""
|
||||
rc = get_test_rc(context, **kw)
|
||||
rc.create()
|
||||
rc.manifest = '{"foo": "bar"}'
|
||||
return rc
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue