Add support for service type=LoadBalancer

Service loadbalancerIP  could be one of the following :
 1. loadbalancerIP allocated from pre-defined pool
    k8s service.spec.type = 'LoadBalancer'
 2. loadbalancerIP specified by user
    k8s service.spec.type = 'LoadBalancer' and service.spec.loadBalancerIP='x.y.z.t'

This commit extend service capability to support '1' and '2'
Implements: blueprint k8s-service-type-loadbalancer

Change-Id: I98f56692e143aa7ab14dd9920139819c7026acce
This commit is contained in:
Yossi Boaron 2017-08-27 12:27:18 +03:00
parent f8a2021f5d
commit 5b3b02bb0b
15 changed files with 1045 additions and 24 deletions

View File

@ -222,6 +222,9 @@ function configure_neutron_defaults {
sg_ids=$(echo $(neutron security-group-list \
--project-id "$project_id" -c id -f value) | tr ' ' ',')
ext_svc_subnet_id="$(neutron subnet-show -c id -f value \
"${KURYR_NEUTRON_DEFAULT_EXT_SVC_SUBNET}")"
local use_octavia
use_octavia=$(trueorfalse True KURYR_K8S_LBAAS_USE_OCTAVIA)
if [[ "$use_octavia" == "True" && \
@ -275,6 +278,7 @@ function configure_neutron_defaults {
if [ -n "$OVS_BRIDGE" ]; then
iniset "$KURYR_CONFIG" neutron_defaults ovs_bridge "$OVS_BRIDGE"
fi
iniset "$KURYR_CONFIG" neutron_defaults external_svc_subnet "$ext_svc_subnet_id"
iniset "$KURYR_CONFIG" octavia_defaults member_mode "$KURYR_K8S_OCTAVIA_MEMBER_MODE"
}

View File

@ -17,6 +17,7 @@ KURYR_NEUTRON_DEFAULT_POD_SUBNET=${KURYR_NEUTRON_DEFAULT_POD_SUBNET:-k8s-pod-sub
KURYR_NEUTRON_DEFAULT_SERVICE_SUBNET=${KURYR_NEUTRON_DEFAULT_SERVICE_SUBNET:-k8s-service-subnet}
KURYR_NEUTRON_DEFAULT_SUBNETPOOL_ID=${KURYR_NEUTRON_DEFAULT_SUBNETPOOL_ID:-}
KURYR_NEUTRON_DEFAULT_ROUTER=${KURYR_NEUTRON_DEFAULT_ROUTER:-}
KURYR_NEUTRON_DEFAULT_EXT_SVC_SUBNET=${KURYR_NEUTRON_DEFAULT_EXT_SVC_SUBNET:-public-subnet}
# Etcd
ETCD_PORT=${ETCD_PORT:-2379}

View File

@ -8,8 +8,10 @@ be implemented in the following way:
* **Service**: It is translated to a single **LoadBalancer** and as many
**Listeners** and **Pools** as ports the Kubernetes Service spec defines.
* **ClusterIP**: It is translated to a LoadBalancer's VIP.
* **loadBalancerIP**: Translated to public IP associated with the LoadBalancer's VIP.
* **Endpoints**: The Endpoint object is translated to a LoadBalancer's VIP.
.. _services: https://kubernetes.io/docs/concepts/services-networking/service/
.. _LBaaS API: https://wiki.openstack.org/wiki/Neutron/LBaaS/API_2.0
@ -396,6 +398,53 @@ The services and pods subnets should be created.
the pod subnet, follow the `Making the Pods be able to reach the Kubernetes
API`_ section
#. For the external services (type=LoadBalancer) case,
two methods are supported:
* Pool - external IPs are allocated from pre-defined pool
* User - user specify the external IP address
In case 'Pool' method should be supported, execute the next steps
A. Create an external/provider network
B. Create subnet/pool range of external CIDR
C. Connect external subnet to kuryr-kubernetes router
D. Configure Kuryr.conf public ip subnet to point to the external subnet::
[neutron_defaults]
external_svc_subnet= external_subnet_id
From this point for each K8s service of type=LoadBalancer and in which
'load-balancer-ip' is not specified, an external IP from
'external_svc_subnet' will be allocated.
For the 'User' case, user should first create an external/floating IP::
$#openstack floating ip create --subnet <ext-subnet-id> <ext-netowrk-id>
$openstack floating ip create --subnet 48ddcfec-1b29-411b-be92-8329cc09fc12 3b4eb25e-e103-491f-a640-a6246d588561
+---------------------------+--------------------------------------+
| Field | Value |
+---------------------+--------------------------------------+
| created_at | 2017-10-02T09:22:37Z |
| description | |
| fixed_ip_address | None |
| floating_ip_address | 172.24.4.13 |
| floating_network_id | 3b4eb25e-e103-491f-a640-a6246d588561 |
| id | 1157e2fd-de64-492d-b955-88ea203b4c37 |
| name | 172.24.4.13 |
| port_id | None |
| project_id | 6556471f4f7b40e2bde1fc6e4aba0eef |
| revision_number | 0 |
| router_id | None |
| status | DOWN |
| updated_at | 2017-10-02T09:22:37Z |
+---------------------+--------------------------------------+
and then create k8s service with type=LoadBalancer and load-balancer-ip=<floating_ip> (e.g: 172.24.4.13)
In both 'User' and 'Pool' methods, the extrenal IP address could be find
in k8s service status information (under loadbalancer/ingress/ip)
Alternative configuration
~~~~~~~~~~~~~~~~~~~~~~~~~
It is actually possible to avoid this routing by performing a deployment change

View File

@ -88,6 +88,10 @@ k8s_opts = [
help=_('Enable port debug to force kuryr port names to be '
'set to their corresponding pod names.'),
default=False),
cfg.StrOpt('service_public_ip_driver',
help=_("The driver that provides external IP for LB at "
"Kubernetes"),
default='neutron_floating_ip'),
]
neutron_defaults = [
@ -104,6 +108,8 @@ neutron_defaults = [
sample_default="br-int"),
cfg.StrOpt('service_subnet',
help=_("Default Neutron subnet ID for Kubernetes services")),
cfg.StrOpt('external_svc_subnet',
help=_("Default external subnet for Kubernetes services")),
]
octavia_defaults = [

View File

@ -420,3 +420,48 @@ class VIFPoolDriver(PodVIFDriver):
vif resources.
"""
raise NotImplementedError()
@six.add_metaclass(abc.ABCMeta)
class ServicePubIpDriver(DriverBase):
"""Manages loadbalancerIP/public ip for neutron lbaas."""
ALIAS = 'service_public_ip'
@abc.abstractmethod
def acquire_service_pub_ip_info(self, spec_type, spec_lb_ip, project_id):
"""Get k8s service loadbalancer IP info based on service spec
:param spec_type: service.spec.type field
:param spec_lb_ip: service spec LoadBlaceIP field
:param project_id: openstack project id
"""
raise NotImplementedError()
@abc.abstractmethod
def release_pub_ip(self, service_pub_ip_info):
"""Release (if needed) based on service_pub_ip_info content
:param service_pub_ip_info: service loadbalancer IP info
"""
raise NotImplementedError()
@abc.abstractmethod
def associate_pub_ip(self, service_pub_ip_info, vip_port_id):
"""Associate loadbalancer IP to lbaas VIP port ID
:param service_pub_ip_info: service loadbalancer IP info
:param vip_port_id: Lbaas VIP port id
"""
raise NotImplementedError()
@abc.abstractmethod
def disassociate_pub_ip(self, service_pub_ip_info):
"""Disassociate loadbalancer IP and lbaas VIP port ID
:param service_pub_ip_info: service loadbalancer IP info
"""

View File

@ -0,0 +1,113 @@
# Copyright (c) 2017 RedHat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from kuryr.lib import exceptions as kl_exc
from kuryr_kubernetes import clients
from kuryr_kubernetes import config
from kuryr_kubernetes.controller.drivers import base
from kuryr_kubernetes.controller.drivers import public_ip
from kuryr_kubernetes.objects import lbaas as obj_lbaas
from oslo_config import cfg
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
class FloatingIpServicePubIPDriver(base.ServicePubIpDriver):
"""Manages floating ip for neutron lbaas.
Service loadbalancerIP support the following :
1. No loadbalancer IP - k8s service.spec.type != 'LoadBalancer'
2. Floating IP allocated from pool -
k8s service.spec.type = 'LoadBalancer' and
service.spec.loadBalancerIP NOT defined
3. Floating IP specified by the user -
k8s service.spec.type = 'LoadBalancer' and
service.spec.loadBalancerIP is defined.
"""
def __init__(self):
super(FloatingIpServicePubIPDriver, self).__init__()
self._drv_pub_ip = public_ip.FipPubIpDriver()
def acquire_service_pub_ip_info(self, spec_type, spec_lb_ip, project_id):
if spec_type != 'LoadBalancer':
return None
if spec_lb_ip:
user_specified_ip = spec_lb_ip.format()
res_id = self._drv_pub_ip.is_ip_available(user_specified_ip)
if res_id:
service_pub_ip_info = (obj_lbaas.LBaaSPubIp(
ip_id=res_id,
ip_addr=str(user_specified_ip),
alloc_method='user'))
return service_pub_ip_info
else:
# user specified IP is not valid
LOG.error("IP=%s is not available", user_specified_ip)
return None
else:
LOG.debug("Trying to allocate public ip from pool")
# get public subnet id from kuryr.conf
external_svc_subnet = config.CONF.neutron_defaults.external_svc_subnet
if not external_svc_subnet:
raise cfg.RequiredOptError('external_svc_subnet',
cfg.OptGroup('neutron_defaults'))
neutron = clients.get_neutron_client()
n_subnet = neutron.show_subnet(external_svc_subnet).get('subnet')
if not n_subnet:
LOG.error(
"No subnet found for external_svc_subnet=%s",
external_svc_subnet)
raise kl_exc.NoResourceException
public_network_id = n_subnet['network_id']
res_id, alloc_ip_addr = (self._drv_pub_ip.allocate_ip
(public_network_id,
external_svc_subnet,
project_id,
'kuryr_lb'))
service_pub_ip_info = obj_lbaas.LBaaSPubIp(ip_id=res_id,
ip_addr=alloc_ip_addr,
alloc_method='pool')
return service_pub_ip_info
def release_pub_ip(self, service_pub_ip_info):
if not service_pub_ip_info:
return
if service_pub_ip_info.alloc_method == 'pool':
retcode = self._drv_pub_ip.free_ip(service_pub_ip_info.ip_id)
if not retcode:
LOG.error("Failed to delete public_ip_id =%s !",
service_pub_ip_info.ip_id)
def associate_pub_ip(self, service_pub_ip_info, vip_port_id):
if (not service_pub_ip_info or
not vip_port_id or
not service_pub_ip_info.ip_id):
return
self._drv_pub_ip.associate(
service_pub_ip_info.ip_id, vip_port_id)
def disassociate_pub_ip(self, service_pub_ip_info):
if not service_pub_ip_info or not service_pub_ip_info.ip_id:
return
self._drv_pub_ip.disassociate(service_pub_ip_info.ip_id)

View File

@ -111,6 +111,21 @@ class LBaaSv2Driver(base.LBaaSDriver):
neutron.delete_lbaas_member,
member.id, member.pool_id)
def _get_vip_port_id(self, loadbalancer):
neutron = clients.get_neutron_client()
try:
fixed_ips = ['subnet_id=%s' % str(loadbalancer.subnet_id),
'ip_address=%s' % str(loadbalancer.ip)]
ports = neutron.list_ports(fixed_ips=fixed_ips)
except n_exc.NeutronClientException as ex:
LOG.error("Port with fixed ips %s not found!", fixed_ips)
raise ex
if ports['ports']:
return ports['ports'][0].get("id")
return None
def _create_loadbalancer(self, loadbalancer):
neutron = clients.get_neutron_client()
response = neutron.create_loadbalancer({'loadbalancer': {
@ -120,6 +135,7 @@ class LBaaSv2Driver(base.LBaaSDriver):
'vip_address': str(loadbalancer.ip),
'vip_subnet_id': loadbalancer.subnet_id}})
loadbalancer.id = response['loadbalancer']['id']
loadbalancer.port_id = self._get_vip_port_id(loadbalancer)
return loadbalancer
def _find_loadbalancer(self, loadbalancer):
@ -133,6 +149,7 @@ class LBaaSv2Driver(base.LBaaSDriver):
try:
loadbalancer.id = response['loadbalancers'][0]['id']
loadbalancer.port_id = self._get_vip_port_id(loadbalancer)
except (KeyError, IndexError):
return None

View File

@ -0,0 +1,143 @@
# Copyright (c) 2017 RedHat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
from kuryr_kubernetes import clients
from neutronclient.common import exceptions as n_exc
from oslo_log import log as logging
import six
LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class BasePubIpDriver(object):
"""Base class for public IP functionality."""
@abc.abstractmethod
def is_ip_available(self, ip_addr):
"""check availability of ip address
:param ip_address:
:returns res_id in case ip is available returns resources id else None
"""
raise NotImplementedError()
@abc.abstractmethod
def allocate_ip(self, pub_net_id, pub_subnet_id, project_id, description):
"""allocate ip address from public network id
:param pub_net_id: public network id
:param pub_subnet_id: public subnet id
:param project_id:
:param description: string describing request
:returns res_id , ip_addr
:res_id - resource id
:ip_addr - ip aaddress
"""
raise NotImplementedError()
@abc.abstractmethod
def free_ip(self, res_id):
"""free ip by resource ID
:param res_id: resource_id
"""
raise NotImplementedError()
@abc.abstractmethod
def associate(self, res_id, vip_port_id):
"""Associate VIP port id with resource_id
:param res_id: id represents pub ip resource
:param vip_port_id: VIP port id
"""
raise NotImplementedError()
@abc.abstractmethod
def disassociate(self, res_id):
"""Clear association between res_id to any vip port
:param res_id: id represents pub ip resource
"""
class FipPubIpDriver(BasePubIpDriver):
"""Floating IP implementation for public IP capability ."""
def is_ip_available(self, ip_addr):
if ip_addr:
neutron = clients.get_neutron_client()
floating_ips_list = neutron.list_floatingips(
floating_ip_address=ip_addr)
for entry in floating_ips_list['floatingips']:
if not entry:
continue
if (entry['floating_ip_address'] == ip_addr and
not entry['port_id']):
return entry['id']
# floating IP not available
LOG.error("Floating IP=%s not available", ip_addr)
else:
LOG.error("Invalid parameter ip_addr=%s", ip_addr)
return None
def allocate_ip(self, pub_net_id, pub_subnet_id, project_id, description):
neutron = clients.get_neutron_client()
try:
response = neutron.create_floatingip({'floatingip': {
'tenant_id': project_id,
'project_id': project_id,
'floating_network_id': pub_net_id,
'subnet_id': pub_subnet_id,
'description': description}})
except n_exc.NeutronClientException as ex:
LOG.error("Failed to create floating IP - subnetid=%s ",
pub_subnet_id)
raise ex
return response['floatingip']['id'], response[
'floatingip']['floating_ip_address']
def free_ip(self, res_id):
neutron = clients.get_neutron_client()
try:
neutron.delete_floatingip(res_id)
except n_exc.NeutronClientException as ex:
LOG.error("Failed to delete floating_ip_id =%s !",
res_id)
raise ex
def _update(self, res_id, vip_port_id):
response = None
neutron = clients.get_neutron_client()
try:
response = neutron.update_floatingip(
res_id, {'floatingip': {'port_id': vip_port_id, }})
except n_exc.NeutronClientException as ex:
LOG.error("Failed to update_floatingip ,floating_ip_id=%s,"
"response=%s!", res_id, response)
raise ex
def associate(self, res_id, vip_port_id):
self._update(res_id, vip_port_id)
def disassociate(self, res_id):
self._update(res_id, None)

View File

@ -27,6 +27,8 @@ from kuryr_kubernetes.objects import lbaas as obj_lbaas
LOG = logging.getLogger(__name__)
SUPPORTED_SERVICE_TYPES = ('ClusterIP', 'LoadBalancer')
class LBaaSSpecHandler(k8s_base.ResourceEventHandler):
"""LBaaSSpecHandler handles K8s Service events.
@ -54,10 +56,14 @@ class LBaaSSpecHandler(k8s_base.ResourceEventHandler):
lbaas_spec = self._generate_lbaas_spec(service)
self._set_lbaas_spec(service, lbaas_spec)
def _get_service_ip(self, service):
def _is_supported_type(self, service):
spec = service['spec']
if spec.get('type') == 'ClusterIP':
return spec.get('clusterIP')
return spec.get('type') in SUPPORTED_SERVICE_TYPES
def _get_service_ip(self, service):
if self._is_supported_type(service):
return service['spec'].get('clusterIP')
return None
def _should_ignore(self, service):
return not(self._has_selector(service))
@ -88,12 +94,16 @@ class LBaaSSpecHandler(k8s_base.ResourceEventHandler):
subnet_id = self._get_subnet_id(service, project_id, ip)
ports = self._generate_lbaas_port_specs(service)
sg_ids = self._drv_sg.get_security_groups(service, project_id)
spec_type = service['spec'].get('type')
spec_lb_ip = service['spec'].get('loadBalancerIP')
return obj_lbaas.LBaaSServiceSpec(ip=ip,
project_id=project_id,
subnet_id=subnet_id,
ports=ports,
security_groups_ids=sg_ids)
security_groups_ids=sg_ids,
type=spec_type,
lb_ip=spec_lb_ip)
def _has_lbaas_spec_changes(self, service, lbaas_spec):
return (self._has_ip_changes(service, lbaas_spec) or
@ -207,6 +217,7 @@ class LoadBalancerHandler(k8s_base.ResourceEventHandler):
self._drv_lbaas = drv_base.LBaaSDriver.get_instance()
self._drv_pod_project = drv_base.PodProjectDriver.get_instance()
self._drv_pod_subnets = drv_base.PodSubnetsDriver.get_instance()
self._drv_service_pub_ip = drv_base.ServicePubIpDriver.get_instance()
def on_present(self, endpoints):
lbaas_spec = self._get_lbaas_spec(endpoints)
@ -452,25 +463,74 @@ class LoadBalancerHandler(k8s_base.ResourceEventHandler):
if l.id not in removed_ids]
return bool(removed_ids)
def _update_lb_status(self, endpoints, lb_ip_address):
status_data = {"loadBalancer": {
"ingress": [{"ip": lb_ip_address.format()}]}}
k8s = clients.get_kubernetes_client()
svc_link = self._get_service_link(endpoints)
try:
k8s.patch_status(svc_link, status_data)
except k_exc.K8sClientException:
# REVISIT(ivc): only raise ResourceNotReady for NotFound
raise k_exc.ResourceNotReady(svc_link)
def _get_service_link(self, endpoints):
ep_link = endpoints['metadata']['selfLink']
link_parts = ep_link.split('/')
if link_parts[-2] != 'endpoints':
raise k_exc.IntegrityError(_(
"Unsupported endpoints link: %(link)s") % {
'link': ep_link})
link_parts[-2] = 'services'
return "/".join(link_parts)
def _sync_lbaas_loadbalancer(self, endpoints, lbaas_state, lbaas_spec):
changed = False
lb = lbaas_state.loadbalancer
if lb and lb.ip != lbaas_spec.ip:
# if loadbalancerIP was associated to lbaas VIP, disassociate it.
if lbaas_state.service_pub_ip_info:
self._drv_service_pub_ip.disassociate_pub_ip(
lbaas_state.service_pub_ip_info)
self._drv_lbaas.release_loadbalancer(
endpoints=endpoints,
loadbalancer=lb)
lb = None
changed = True
if not lb and lbaas_spec.ip:
lb = self._drv_lbaas.ensure_loadbalancer(
endpoints=endpoints,
project_id=lbaas_spec.project_id,
subnet_id=lbaas_spec.subnet_id,
ip=lbaas_spec.ip,
security_groups_ids=lbaas_spec.security_groups_ids)
changed = True
if not lb:
if lbaas_spec.ip:
lb = self._drv_lbaas.ensure_loadbalancer(
endpoints=endpoints,
project_id=lbaas_spec.project_id,
subnet_id=lbaas_spec.subnet_id,
ip=lbaas_spec.ip,
security_groups_ids=lbaas_spec.security_groups_ids)
if lbaas_state.service_pub_ip_info is None:
service_pub_ip_info = (
self._drv_service_pub_ip.acquire_service_pub_ip_info(
lbaas_spec.type,
lbaas_spec.lb_ip,
lbaas_spec.project_id))
if service_pub_ip_info:
# if loadbalancerIP should be defined for lbaas,
# associate it to lbaas VIP
# and update k8s-service status with
# loadbalancerIP address
self._drv_service_pub_ip.associate_pub_ip(
service_pub_ip_info, lb.port_id)
self._update_lb_status(
endpoints, service_pub_ip_info.ip_addr)
lbaas_state.service_pub_ip_info = service_pub_ip_info
changed = True
elif lbaas_state.service_pub_ip_info:
self._drv_service_pub_ip.release_pub_ip(
lbaas_state.service_pub_ip_info)
lbaas_state.service_pub_ip_info = None
changed = True
lbaas_state.loadbalancer = lb
return changed

View File

@ -78,6 +78,29 @@ class K8sClient(object):
raise exc.K8sClientException(response.text)
return response.json()
def _get_url_and_header(self, path):
url = self._base_url + path
header = {'Content-Type': 'application/merge-patch+json',
'Accept': 'application/json'}
if self.token:
header.update({'Authorization': 'Bearer %s' % self.token})
return url, header
def patch_status(self, path, data):
LOG.debug("Patch_status %(path)s: %(data)s", {
'path': path, 'data': data})
path = path + '/status'
url, header = self._get_url_and_header(path)
response = requests.patch(url, json={"status": data},
headers=header, cert=self.cert,
verify=self.verify_server)
if response.ok:
return response.json().get('status')
raise exc.K8sClientException(response.text)
def annotate(self, path, annotations, resource_version=None):
"""Pushes a resource annotation to the K8s API resource
@ -88,11 +111,9 @@ class K8sClient(object):
"""
LOG.debug("Annotate %(path)s: %(names)s", {
'path': path, 'names': list(annotations)})
url = self._base_url + path
header = {'Content-Type': 'application/merge-patch+json',
'Accept': 'application/json'}
if self.token:
header.update({'Authorization': 'Bearer %s' % self.token})
url, header = self._get_url_and_header(path)
while itertools.count(1):
data = jsonutils.dumps({
"metadata": {

View File

@ -30,6 +30,7 @@ class LBaaSLoadBalancer(k_obj.KuryrK8sObjectBase):
'name': obj_fields.StringField(),
'ip': obj_fields.IPAddressField(),
'subnet_id': obj_fields.UUIDField(),
'port_id': obj_fields.UUIDField(),
}
@ -76,6 +77,17 @@ class LBaaSMember(k_obj.KuryrK8sObjectBase):
}
@obj_base.VersionedObjectRegistry.register
class LBaaSPubIp(k_obj.KuryrK8sObjectBase):
VERSION = '1.0'
fields = {
'ip_id': obj_fields.UUIDField(),
'ip_addr': obj_fields.IPAddressField(),
'alloc_method': obj_fields.StringField(),
}
@obj_base.VersionedObjectRegistry.register
class LBaaSState(k_obj.KuryrK8sObjectBase):
VERSION = '1.0'
@ -90,6 +102,9 @@ class LBaaSState(k_obj.KuryrK8sObjectBase):
default=[]),
'members': obj_fields.ListOfObjectsField(LBaaSMember.__name__,
default=[]),
'service_pub_ip_info': obj_fields.ObjectField(LBaaSPubIp.__name__,
nullable=True,
default=None),
}
@ -115,4 +130,6 @@ class LBaaSServiceSpec(k_obj.KuryrK8sObjectBase):
'project_id': obj_fields.StringField(nullable=True, default=None),
'subnet_id': obj_fields.UUIDField(nullable=True, default=None),
'security_groups_ids': k_fields.ListOfUUIDField(default=[]),
'type': obj_fields.StringField(nullable=True, default=None),
'lb_ip': obj_fields.IPAddressField(nullable=True, default=None),
}

View File

@ -0,0 +1,327 @@
# Copyright (c) 2017 RedHat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from kuryr.lib import exceptions as kl_exc
import mock
from neutronclient.common import exceptions as n_exc
from kuryr_kubernetes.controller.drivers import lb_public_ip\
as d_lb_public_ip
from kuryr_kubernetes.controller.drivers import public_ip
from kuryr_kubernetes.objects import lbaas as obj_lbaas
from kuryr_kubernetes.tests import base as test_base
from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
from oslo_config import cfg
class TestFloatingIpServicePubIPDriverDriver(test_base.TestCase):
def test_acquire_service_pub_ip_info_clusterip(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
project_id = mock.sentinel.project_id
cur_service_pub_ip_info = None
service = {'spec': {'type': 'ClusterIP'}}
self.assertIsNone(cls.acquire_service_pub_ip_info
(m_driver, service, project_id,
cur_service_pub_ip_info))
def test_acquire_service_pub_ip_info_usr_specified_ip(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.4', 'port_id': None,
'id': 'a2a62ea7-e3bf-40df-8c09-aa0c29876a6b'}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = '1.2.3.4'
expected_resp = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='user')
self.assertEqual(expected_resp, cls.acquire_service_pub_ip_info
(m_driver, spec_type, spec_lb_ip, project_id))
def test_acquire_service_pub_ip_info_user_specified_non_exist_fip(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.5',
'port_id': None}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = '1.2.3.4'
self.assertIsNone(cls.acquire_service_pub_ip_info
(m_driver, spec_type,
spec_lb_ip, project_id))
def test_acquire_service_pub_ip_info_user_specified_occupied_fip(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.4',
'port_id': 'ec29d641-fec4-4f67-928a-124a76b3a8e6'}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = '1.2.3.4'
self.assertIsNone(cls.acquire_service_pub_ip_info
(m_driver, spec_type,
spec_lb_ip, project_id))
@mock.patch('kuryr_kubernetes.config.CONF')
def test_acquire_service_pub_ip_info_pool_subnet_not_defined(self, m_cfg):
driver = d_lb_public_ip.FloatingIpServicePubIPDriver()
public_subnet = ''
m_cfg.neutron_defaults.external_svc_subnet = public_subnet
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.list_floatingips.return_value = {}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = None
self.assertRaises(
cfg.RequiredOptError,
driver.acquire_service_pub_ip_info,
spec_type, spec_lb_ip, project_id)
@mock.patch('kuryr_kubernetes.config.CONF')
def test_acquire_service_pub_ip_info_pool_subnet_not_exist(self, m_cfg):
driver = d_lb_public_ip.FloatingIpServicePubIPDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
public_subnet = mock.sentinel.public_subnet
m_cfg.neutron_defaults.external_svc_subnet = public_subnet
neutron.show_subnet.return_value = {}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = None
self.assertRaises(
kl_exc.NoResourceException,
driver.acquire_service_pub_ip_info,
spec_type, spec_lb_ip, project_id)
@mock.patch('kuryr_kubernetes.config.CONF')
def test_acquire_service_pub_ip_info_alloc_from_pool(self, m_cfg):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
m_cfg.neutron_defaults.external_svc_subnet =\
mock.sentinel.external_svc_subnet
neutron.show_subnet.return_value =\
{'subnet': {'network_id': 'ec29d641-fec4-4f67-928a-124a76b3a8e6'}}
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
neutron.create_floatingip.return_value = {'floatingip': floating_ip}
project_id = mock.sentinel.project_id
spec_type = 'LoadBalancer'
spec_lb_ip = None
expected_resp = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
self.assertEqual(expected_resp, cls.acquire_service_pub_ip_info
(m_driver, spec_type, spec_lb_ip, project_id))
def test_release_pub_ip_empty_lb_ip_info(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
service_pub_ip_info = None
self.assertIsNone(cls.release_pub_ip
(m_driver, service_pub_ip_info))
def test_release_pub_ip_alloc_method_non_pool(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='kk')
self.assertIsNone(
cls.release_pub_ip(m_driver, service_pub_ip_info))
def test_release_pub_ip_alloc_method_user(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='user')
self.assertIsNone(cls.release_pub_ip
(m_driver, service_pub_ip_info))
def test_release_pub_ip_alloc_method_pool_neutron_exception(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.delete_floatingip.side_effect = n_exc.NeutronClientException
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
self.assertRaises(
n_exc.NeutronClientException, cls.release_pub_ip,
m_driver, service_pub_ip_info)
def test_release_pub_ip_alloc_method_pool_neutron_succeeded(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.delete_floatingip.return_value = None
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
self.assertIsNone(cls.release_pub_ip
(m_driver, service_pub_ip_info))
def test_associate_pub_ip_empty_params(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
service_pub_ip_info = None
vip_port_id = None
self.assertIsNone(cls.associate_pub_ip
(m_driver, service_pub_ip_info, vip_port_id))
def test_associate_lb_fip_id_not_exist(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=0,
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
vip_port_id = 'ec29d641-fec4-4f67-928a-124a76b3a777'
self.assertIsNone(cls.associate_pub_ip
(m_driver, service_pub_ip_info, vip_port_id))
def test_associate_lb_fip_id_not_exist_neutron_exception(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.side_effect = n_exc.NeutronClientException
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
vip_port_id = 'ec29d641-fec4-4f67-928a-124a76b3a777'
self.assertRaises(
n_exc.NeutronClientException, cls.associate_pub_ip,
m_driver, service_pub_ip_info, vip_port_id)
def test_disassociate_pub_ip_empty_param(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
service_pub_ip_info = None
self.assertIsNone(cls.disassociate_pub_ip
(m_driver, service_pub_ip_info))
def test_disassociate_pub_ip_fip_id_not_exist(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=0,
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
self.assertIsNone(cls.disassociate_pub_ip
(m_driver, service_pub_ip_info))
def test_disassociate_pub_ip_neutron_exception(self):
cls = d_lb_public_ip.FloatingIpServicePubIPDriver
m_driver = mock.Mock(spec=cls)
m_driver._drv_pub_ip = public_ip.FipPubIpDriver()
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.side_effect = n_exc.NeutronClientException
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
service_pub_ip_info = \
obj_lbaas.LBaaSPubIp(ip_id=floating_ip['id'],
ip_addr=floating_ip['floating_ip_address'],
alloc_method='pool')
self.assertRaises(
n_exc.NeutronClientException, cls.disassociate_pub_ip,
m_driver, service_pub_ip_info)

View File

@ -0,0 +1,188 @@
# Copyright (c) 2017 RedHat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from neutronclient.common import exceptions as n_exc
from kuryr_kubernetes.controller.drivers import public_ip\
as d_public_ip
from kuryr_kubernetes.tests import base as test_base
from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
class TestFipPubIpDriver(test_base.TestCase):
def test_is_ip_available_none_param(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
fip_ip_addr = None
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertIsNone(fip_id)
def test_is_ip_available_empty_param(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
fip_ip_addr = None
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertIsNone(fip_id)
def test_is_ip_available_ip_not_exist(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.4', 'port_id': None,
'id': 'a2a62ea7-e3bf-40df-8c09-aa0c29876a6b'}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
fip_ip_addr = '1.1.1.1'
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertIsNone(fip_id)
def test_is_ip_available_empty_fip_list(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = None
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
fip_ip_addr = '1.1.1.1'
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertIsNone(fip_id)
def test_is_ip_available_occupied_fip(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.4',
'port_id': 'ec29d641-fec4-4f67-928a-124a76b3a8e6'}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
fip_ip_addr = '1.2.3.4'
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertIsNone(fip_id)
def test_is_ip_available_ip_exist_and_available(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.4', 'port_id': None,
'id': 'a2a62ea7-e3bf-40df-8c09-aa0c29876a6b'}
neutron.list_floatingips.return_value = {'floatingips': [floating_ip]}
fip_ip_addr = '1.2.3.4'
fip_id = cls.is_ip_available(m_driver, fip_ip_addr)
self.assertEqual(fip_id, 'a2a62ea7-e3bf-40df-8c09-aa0c29876a6b')
def test_allocate_ip_all_green(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
pub_net_id = mock.sentinel.pub_net_id
pub_subnet_id = mock.sentinel.pub_subnet_id
project_id = mock.sentinel.project_id
description = mock.sentinel.description
neutron = self.useFixture(k_fix.MockNeutronClient()).client
floating_ip = {'floating_ip_address': '1.2.3.5',
'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'}
neutron.create_floatingip.return_value = {'floatingip': floating_ip}
fip_id, fip_addr = cls.allocate_ip(
m_driver, pub_net_id, pub_subnet_id, project_id, description)
self.assertEqual(fip_id, floating_ip['id'])
self.assertEqual(fip_addr, floating_ip['floating_ip_address'])
def test_allocate_ip_neutron_exception(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
pub_net_id = mock.sentinel.pub_net_id
pub_subnet_id = mock.sentinel.pub_subnet_id
project_id = mock.sentinel.project_id
description = mock.sentinel.description
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.create_floatingip.side_effect = n_exc.NeutronClientException
self.assertRaises(
n_exc.NeutronClientException, cls.allocate_ip,
m_driver, pub_net_id, pub_subnet_id, project_id, description)
def test_free_ip_neutron_exception(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.delete_floatingip.side_effect = n_exc.NeutronClientException
self.assertRaises(
n_exc.NeutronClientException, cls.free_ip, m_driver, res_id)
def test_free_ip_succeeded(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.delete_floatingip.return_value = None
try:
cls.free_ip(m_driver, res_id)
except Exception:
self.fail("Encountered an unexpected exception.")
def test_associate_neutron_exception(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
vip_port_id = mock.sentinel.vip_port_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.side_effect = n_exc.NeutronClientException
retcode = cls.associate(m_driver, res_id, vip_port_id)
self.assertIsNone(retcode)
def test_associate_succeeded(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
vip_port_id = mock.sentinel.vip_port_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
retcode = cls.associate(m_driver, res_id, vip_port_id)
self.assertIsNone(retcode)
def test_disassociate_neutron_exception(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.side_effect = n_exc.NeutronClientException
self.assertIsNone(cls.disassociate
(m_driver, res_id))
def test_disassociate_succeeded(self):
cls = d_public_ip.FipPubIpDriver
m_driver = mock.Mock(spec=cls)
res_id = mock.sentinel.res_id
neutron = self.useFixture(k_fix.MockNeutronClient()).client
neutron.update_floatingip.return_value = None
self.assertIsNone(cls.disassociate
(m_driver, res_id))

View File

@ -39,7 +39,6 @@ class TestLBaaSSpecHandler(test_base.TestCase):
m_get_drv_project.return_value = mock.sentinel.drv_project
m_get_drv_subnets.return_value = mock.sentinel.drv_subnets
m_get_drv_sg.return_value = mock.sentinel.drv_sg
handler = h_lbaas.LBaaSSpecHandler()
self.assertEqual(mock.sentinel.drv_project, handler._drv_project)
@ -51,11 +50,16 @@ class TestLBaaSSpecHandler(test_base.TestCase):
old_spec = mock.sentinel.old_spec
new_spec = mock.sentinel.new_spec
project_id = mock.sentinel.project_id
m_drv_project = mock.Mock()
m_drv_project.get_project.return_value = project_id
m_handler = mock.Mock(spec=h_lbaas.LBaaSSpecHandler)
m_handler._get_lbaas_spec.return_value = old_spec
m_handler._has_lbaas_spec_changes.return_value = True
m_handler._generate_lbaas_spec.return_value = new_spec
m_handler._should_ignore.return_value = False
m_handler._drv_project = m_drv_project
h_lbaas.LBaaSSpecHandler.on_present(m_handler, svc_event)
@ -105,13 +109,28 @@ class TestLBaaSSpecHandler(test_base.TestCase):
ret = h_lbaas.LBaaSSpecHandler._get_service_ip(m_handler, svc_body)
self.assertEqual(mock.sentinel.cluster_ip, ret)
def test_get_service_ip_not_cluster_ip(self):
svc_body = {'spec': {'type': 'notClusterIP',
svc_body = {'spec': {'type': 'LoadBalancer',
'clusterIP': mock.sentinel.cluster_ip}}
m_handler = mock.Mock(spec=h_lbaas.LBaaSSpecHandler)
ret = h_lbaas.LBaaSSpecHandler._get_service_ip(m_handler, svc_body)
self.assertIsNone(ret)
self.assertEqual(mock.sentinel.cluster_ip, ret)
def test_is_supported_type_clusterip(self):
m_handler = mock.Mock(spec=h_lbaas.LBaaSSpecHandler)
svc_body = {'spec': {'type': 'ClusterIP',
'clusterIP': mock.sentinel.cluster_ip}}
ret = h_lbaas.LBaaSSpecHandler._is_supported_type(m_handler, svc_body)
self.assertEqual(ret, True)
def test_is_supported_type_loadbalancer(self):
m_handler = mock.Mock(spec=h_lbaas.LBaaSSpecHandler)
svc_body = {'spec': {'type': 'LoadBalancer',
'clusterIP': mock.sentinel.cluster_ip}}
ret = h_lbaas.LBaaSSpecHandler._is_supported_type(m_handler, svc_body)
self.assertEqual(ret, True)
def _make_test_net_obj(self, cidr_list):
subnets = [osv_subnet.Subnet(cidr=cidr) for cidr in cidr_list]
@ -141,6 +160,8 @@ class TestLBaaSSpecHandler(test_base.TestCase):
spec_ctor_path = 'kuryr_kubernetes.objects.lbaas.LBaaSServiceSpec'
with mock.patch(spec_ctor_path) as m_spec_ctor:
m_spec_ctor.return_value = mock.sentinel.ret_obj
service = {'spec': {'type': 'ClusterIP'}}
ret_obj = h_lbaas.LBaaSSpecHandler._generate_lbaas_spec(
m_handler, service)
self.assertEqual(mock.sentinel.ret_obj, ret_obj)
@ -149,7 +170,9 @@ class TestLBaaSSpecHandler(test_base.TestCase):
project_id=project_id,
subnet_id=subnet_id,
ports=ports,
security_groups_ids=sg_ids)
security_groups_ids=sg_ids,
type='ClusterIP',
lb_ip=None)
m_drv_project.get_project.assert_called_once_with(service)
m_handler._get_service_ip.assert_called_once_with(service)
@ -358,22 +381,26 @@ class FakeLBaaSDriver(drv_base.LBaaSDriver):
class TestLoadBalancerHandler(test_base.TestCase):
@mock.patch('kuryr_kubernetes.controller.drivers.base'
'.ServicePubIpDriver.get_instance')
@mock.patch('kuryr_kubernetes.controller.drivers.base'
'.PodSubnetsDriver.get_instance')
@mock.patch('kuryr_kubernetes.controller.drivers.base'
'.PodProjectDriver.get_instance')
@mock.patch('kuryr_kubernetes.controller.drivers.base'
'.LBaaSDriver.get_instance')
def test_init(self, m_get_drv_lbaas, m_get_drv_project, m_get_drv_subnets):
def test_init(self, m_get_drv_lbaas, m_get_drv_project,
m_get_drv_subnets, m_get_drv_service_pub_ip):
m_get_drv_lbaas.return_value = mock.sentinel.drv_lbaas
m_get_drv_project.return_value = mock.sentinel.drv_project
m_get_drv_subnets.return_value = mock.sentinel.drv_subnets
m_get_drv_service_pub_ip.return_value = mock.sentinel.drv_lb_ip
handler = h_lbaas.LoadBalancerHandler()
self.assertEqual(mock.sentinel.drv_lbaas, handler._drv_lbaas)
self.assertEqual(mock.sentinel.drv_project, handler._drv_pod_project)
self.assertEqual(mock.sentinel.drv_subnets, handler._drv_pod_subnets)
self.assertEqual(mock.sentinel.drv_lb_ip, handler._drv_service_pub_ip)
def test_on_present(self):
lbaas_spec = mock.sentinel.lbaas_spec

3
setup.cfg Normal file → Executable file
View File

@ -65,6 +65,9 @@ kuryr_kubernetes.controller.drivers.pod_vif =
kuryr_kubernetes.controller.drivers.endpoints_lbaas =
lbaasv2 = kuryr_kubernetes.controller.drivers.lbaasv2:LBaaSv2Driver
kuryr_kubernetes.controller.drivers.service_public_ip =
neutron_floating_ip = kuryr_kubernetes.controller.drivers.lb_public_ip:FloatingIpServicePubIPDriver
kuryr_kubernetes.controller.drivers.vif_pool =
noop = kuryr_kubernetes.controller.drivers.vif_pool:NoopVIFPool
neutron = kuryr_kubernetes.controller.drivers.vif_pool:NeutronVIFPool