OCP-Router: OCP-Route and Ingress LBaaS handlers
This is the third patch of the Ingress Controller capability. This patch implements OCP-Route and Ingress LBaaS handlers. Those handlers should retrieve the L7 LB details from the Ingress controller and update L7 policy/rules and pool/members upon changes in OCP-route and k8S-endpoint resources. Please follow the instructions below to verify OCP-Router functionality: https://docs.google.com/document/d/1c3mfBToBbWlwFcw3S8fr7pQZb5_YZqFYdlG1HqaQPkQ/edit?usp=sharing Implements: blueprint openshift-router-support Change-Id: Ibfb6cda6dde9613ad31859d38235be031ade0639
This commit is contained in:
parent
4ab102afa8
commit
d5902e8fed
@ -31,6 +31,10 @@ K8S_ANNOTATION_VIF = K8S_ANNOTATION_PREFIX + '-vif'
|
||||
K8S_ANNOTATION_LBAAS_SPEC = K8S_ANNOTATION_PREFIX + '-lbaas-spec'
|
||||
K8S_ANNOTATION_LBAAS_STATE = K8S_ANNOTATION_PREFIX + '-lbaas-state'
|
||||
K8S_ANNOTATION_NET_CRD = K8S_ANNOTATION_PREFIX + '-net-crd'
|
||||
K8S_ANNOTATION_LBAAS_RT_STATE = K8S_ANNOTATION_PREFIX + '-lbaas-route-state'
|
||||
K8S_ANNOTATION_LBAAS_RT_NOTIF = K8S_ANNOTATION_PREFIX + '-lbaas-route-notif'
|
||||
K8S_ANNOTATION_ROUTE_STATE = K8S_ANNOTATION_PREFIX + '-route-state'
|
||||
K8S_ANNOTATION_ROUTE_SPEC = K8S_ANNOTATION_PREFIX + '-route-spec'
|
||||
|
||||
K8S_OS_VIF_NOOP_PLUGIN = "noop"
|
||||
|
||||
|
212
kuryr_kubernetes/controller/handlers/ingress_lbaas.py
Normal file
212
kuryr_kubernetes/controller/handlers/ingress_lbaas.py
Normal file
@ -0,0 +1,212 @@
|
||||
# Copyright (c) 2018 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 oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from kuryr_kubernetes import clients
|
||||
from kuryr_kubernetes import config
|
||||
from kuryr_kubernetes import constants as k_const
|
||||
from kuryr_kubernetes.controller.drivers import base as drv_base
|
||||
from kuryr_kubernetes.controller.handlers import lbaas as h_lbaas
|
||||
from kuryr_kubernetes.controller.ingress import ingress_ctl
|
||||
from kuryr_kubernetes.objects import lbaas as obj_lbaas
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IngressLoadBalancerHandler(h_lbaas.LoadBalancerHandler):
|
||||
"""IngressLoadBalancerHandler handles K8s Endpoints events.
|
||||
|
||||
IngressLoadBalancerHandler handles K8s Endpoints events and tracks
|
||||
changes in LBaaSServiceSpec to update Ingress Controller
|
||||
L7 router accordingly.
|
||||
"""
|
||||
|
||||
OBJECT_KIND = k_const.K8S_OBJ_ENDPOINTS
|
||||
OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "endpoints")
|
||||
|
||||
def __init__(self):
|
||||
super(IngressLoadBalancerHandler, self).__init__()
|
||||
self._drv_lbaas = drv_base.LBaaSDriver.get_instance()
|
||||
self._l7_router = None
|
||||
|
||||
def _should_ignore(self, endpoints, lbaas_spec):
|
||||
return not(lbaas_spec and
|
||||
self._has_pods(endpoints))
|
||||
|
||||
def on_present(self, endpoints):
|
||||
if not self._l7_router:
|
||||
ing_ctl = ingress_ctl.IngressCtrlr.get_instance()
|
||||
self._l7_router, listener = ing_ctl.get_router_and_listener()
|
||||
if not self._l7_router:
|
||||
LOG.info("No L7 router found - do nothing")
|
||||
return
|
||||
|
||||
lbaas_spec = self._get_lbaas_spec(endpoints)
|
||||
if self._should_ignore(endpoints, lbaas_spec):
|
||||
return
|
||||
|
||||
pool_name = self._drv_lbaas.get_loadbalancer_pool_name(
|
||||
self._l7_router, endpoints['metadata']['namespace'],
|
||||
endpoints['metadata']['name'])
|
||||
pool = self._drv_lbaas.get_pool_by_name(pool_name,
|
||||
self._l7_router.project_id)
|
||||
if not pool:
|
||||
if self._get_lbaas_route_state(endpoints):
|
||||
self._set_lbaas_route_state(endpoints, None)
|
||||
LOG.debug("L7 routing: no route defined for service "
|
||||
":%s - do nothing", endpoints['metadata']['name'])
|
||||
else:
|
||||
# pool was found in L7 router LB ,verify that members are up2date
|
||||
lbaas_route_state = self._get_lbaas_route_state(endpoints)
|
||||
if not lbaas_route_state:
|
||||
lbaas_route_state = obj_lbaas.LBaaSRouteState()
|
||||
lbaas_route_state.pool = pool
|
||||
if self._sync_lbaas_route_members(endpoints,
|
||||
lbaas_route_state, lbaas_spec):
|
||||
self._set_lbaas_route_state(endpoints, lbaas_route_state)
|
||||
self._clear_route_notification(endpoints)
|
||||
|
||||
def on_deleted(self, endpoints):
|
||||
if not self._l7_router:
|
||||
LOG.info("No L7 router found - do nothing")
|
||||
return
|
||||
|
||||
lbaas_route_state = self._get_lbaas_route_state(endpoints)
|
||||
if not lbaas_route_state:
|
||||
return
|
||||
self._remove_unused_route_members(endpoints, lbaas_route_state,
|
||||
obj_lbaas.LBaaSServiceSpec())
|
||||
|
||||
def _sync_lbaas_route_members(self, endpoints,
|
||||
lbaas_route_state, lbaas_spec):
|
||||
changed = False
|
||||
if self._remove_unused_route_members(
|
||||
endpoints, lbaas_route_state, lbaas_spec):
|
||||
changed = True
|
||||
|
||||
if self._add_new_route_members(endpoints, lbaas_route_state):
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
def _add_new_route_members(self, endpoints, lbaas_route_state):
|
||||
changed = False
|
||||
|
||||
current_targets = {(str(m.ip), m.port)
|
||||
for m in lbaas_route_state.members}
|
||||
|
||||
for subset in endpoints.get('subsets', []):
|
||||
subset_ports = subset.get('ports', [])
|
||||
for subset_address in subset.get('addresses', []):
|
||||
try:
|
||||
target_ip = subset_address['ip']
|
||||
target_ref = subset_address['targetRef']
|
||||
if target_ref['kind'] != k_const.K8S_OBJ_POD:
|
||||
continue
|
||||
except KeyError:
|
||||
continue
|
||||
for subset_port in subset_ports:
|
||||
target_port = subset_port['port']
|
||||
if (target_ip, target_port) in current_targets:
|
||||
continue
|
||||
|
||||
# TODO(apuimedo): Do not pass subnet_id at all when in
|
||||
# L3 mode once old neutron-lbaasv2 is not supported, as
|
||||
# octavia does not require it
|
||||
if (config.CONF.octavia_defaults.member_mode ==
|
||||
k_const.OCTAVIA_L2_MEMBER_MODE):
|
||||
member_subnet_id = self._get_pod_subnet(target_ref,
|
||||
target_ip)
|
||||
else:
|
||||
# We use the service subnet id so that the connectivity
|
||||
# from VIP to pods happens in layer 3 mode, i.e.,
|
||||
# routed.
|
||||
member_subnet_id = self._l7_router.subnet_id
|
||||
member = self._drv_lbaas.ensure_member(
|
||||
loadbalancer=self._l7_router,
|
||||
pool=lbaas_route_state.pool,
|
||||
subnet_id=member_subnet_id,
|
||||
ip=target_ip,
|
||||
port=target_port,
|
||||
target_ref_namespace=target_ref['namespace'],
|
||||
target_ref_name=target_ref['name'])
|
||||
|
||||
lbaas_route_state.members.append(member)
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
def _remove_unused_route_members(
|
||||
self, endpoints, lbaas_route_state, lbaas_spec):
|
||||
spec_port_names = {p.name for p in lbaas_spec.ports}
|
||||
current_targets = {(a['ip'], p['port'])
|
||||
for s in endpoints['subsets']
|
||||
for a in s['addresses']
|
||||
for p in s['ports']
|
||||
if p.get('name') in spec_port_names}
|
||||
removed_ids = set()
|
||||
for member in lbaas_route_state.members:
|
||||
if (str(member.ip), member.port) in current_targets:
|
||||
continue
|
||||
self._drv_lbaas.release_member(self._l7_router, member)
|
||||
removed_ids.add(member.id)
|
||||
if removed_ids:
|
||||
lbaas_route_state.members = [
|
||||
m for m in lbaas_route_state.members
|
||||
if m.id not in removed_ids]
|
||||
return bool(removed_ids)
|
||||
|
||||
def _set_lbaas_route_state(self, endpoints, route_state):
|
||||
if route_state is None:
|
||||
LOG.debug("Removing LBaaSRouteState annotation: %r", route_state)
|
||||
annotation = None
|
||||
else:
|
||||
route_state.obj_reset_changes(recursive=True)
|
||||
LOG.debug("Setting LBaaSRouteState annotation: %r", route_state)
|
||||
annotation = jsonutils.dumps(route_state.obj_to_primitive(),
|
||||
sort_keys=True)
|
||||
k8s = clients.get_kubernetes_client()
|
||||
k8s.annotate(endpoints['metadata']['selfLink'],
|
||||
{k_const.K8S_ANNOTATION_LBAAS_RT_STATE: annotation},
|
||||
resource_version=endpoints['metadata']['resourceVersion'])
|
||||
|
||||
def _get_lbaas_route_state(self, endpoints):
|
||||
try:
|
||||
annotations = endpoints['metadata']['annotations']
|
||||
annotation = annotations[k_const.K8S_ANNOTATION_LBAAS_RT_STATE]
|
||||
except KeyError:
|
||||
return None
|
||||
obj_dict = jsonutils.loads(annotation)
|
||||
obj = obj_lbaas.LBaaSRouteState.obj_from_primitive(obj_dict)
|
||||
LOG.debug("Got LBaaSRouteState from annotation: %r", obj)
|
||||
return obj
|
||||
|
||||
def _clear_route_notification(self, endpoints):
|
||||
try:
|
||||
annotations = endpoints['metadata']['annotations']
|
||||
annotation = annotations[
|
||||
k_const.K8S_ANNOTATION_LBAAS_RT_NOTIF]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
LOG.debug("Removing LBaaSRouteNotifier annotation")
|
||||
annotation = None
|
||||
k8s = clients.get_kubernetes_client()
|
||||
k8s.annotate(
|
||||
endpoints['metadata']['selfLink'],
|
||||
{k_const.K8S_ANNOTATION_LBAAS_RT_NOTIF: annotation},
|
||||
resource_version=endpoints['metadata']['resourceVersion'])
|
@ -167,3 +167,35 @@ class LBaaSL7Rule(k_obj.KuryrK8sObjectBase):
|
||||
'type': obj_fields.StringField(nullable=True),
|
||||
'value': obj_fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class LBaaSRouteState(k_obj.KuryrK8sObjectBase):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'members': obj_fields.ListOfObjectsField(LBaaSMember.__name__,
|
||||
default=[]),
|
||||
'pool': obj_fields.ObjectField(LBaaSPool.__name__,
|
||||
nullable=True, default=None),
|
||||
}
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class LBaaSRouteNotifEntry(k_obj.KuryrK8sObjectBase):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'route_id': obj_fields.UUIDField(),
|
||||
'msg': obj_fields.StringField(),
|
||||
}
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class LBaaSRouteNotifier(k_obj.KuryrK8sObjectBase):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'routes': obj_fields.ListOfObjectsField(
|
||||
LBaaSRouteNotifEntry.__name__, default=[]),
|
||||
}
|
||||
|
43
kuryr_kubernetes/objects/route.py
Normal file
43
kuryr_kubernetes/objects/route.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2018 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_kubernetes.objects import base as k_obj
|
||||
from kuryr_kubernetes.objects import lbaas as lbaas_obj
|
||||
from oslo_versionedobjects import base as obj_base
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class RouteState(k_obj.KuryrK8sObjectBase):
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'router_pool': obj_fields.ObjectField(
|
||||
lbaas_obj.LBaaSPool.__name__, nullable=True, default=None),
|
||||
'l7_policy': obj_fields.ObjectField(
|
||||
lbaas_obj.LBaaSL7Policy.__name__, nullable=True, default=None),
|
||||
'h_l7_rule': obj_fields.ObjectField(
|
||||
lbaas_obj.LBaaSL7Rule.__name__, nullable=True, default=None),
|
||||
'p_l7_rule': obj_fields.ObjectField(
|
||||
lbaas_obj.LBaaSL7Rule.__name__, nullable=True, default=None),
|
||||
}
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class RouteSpec(k_obj.KuryrK8sObjectBase):
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'host': obj_fields.StringField(nullable=True, default=None),
|
||||
'path': obj_fields.StringField(nullable=True, default=None),
|
||||
'to_service': obj_fields.StringField(nullable=True, default=None),
|
||||
}
|
0
kuryr_kubernetes/platform/__init__.py
Normal file
0
kuryr_kubernetes/platform/__init__.py
Normal file
17
kuryr_kubernetes/platform/constants.py
Normal file
17
kuryr_kubernetes/platform/constants.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2018 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.
|
||||
|
||||
OCP_API_BASE = '/oapi/v1'
|
||||
OCP_OBJ_ROUTE = 'Route'
|
0
kuryr_kubernetes/platform/ocp/__init__.py
Normal file
0
kuryr_kubernetes/platform/ocp/__init__.py
Normal file
255
kuryr_kubernetes/platform/ocp/controller/handlers/route.py
Normal file
255
kuryr_kubernetes/platform/ocp/controller/handlers/route.py
Normal file
@ -0,0 +1,255 @@
|
||||
# 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._i18n import _
|
||||
|
||||
from kuryr_kubernetes import clients
|
||||
from kuryr_kubernetes import constants as k_const
|
||||
from kuryr_kubernetes.controller.drivers import base as drv_base
|
||||
from kuryr_kubernetes.controller.ingress import ingress_ctl
|
||||
from kuryr_kubernetes import exceptions as k_exc
|
||||
from kuryr_kubernetes.handlers import k8s_base
|
||||
from kuryr_kubernetes.objects import lbaas as obj_lbaas
|
||||
from kuryr_kubernetes.objects import route as obj_route
|
||||
from kuryr_kubernetes.platform import constants as ocp_const
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OcpRouteHandler(k8s_base.ResourceEventHandler):
|
||||
"""OcpRouteHandler handles OCP route events.
|
||||
|
||||
An OpenShift route allows service to be externally-reachable via host name.
|
||||
This host name is then used to route traffic to the service.
|
||||
The OcpRouteHandler is responsible for processing all route resource
|
||||
events.
|
||||
|
||||
"""
|
||||
|
||||
OBJECT_KIND = ocp_const.OCP_OBJ_ROUTE
|
||||
OBJECT_WATCH_PATH = "%s/%s" % (ocp_const.OCP_API_BASE, "routes")
|
||||
|
||||
def __init__(self):
|
||||
self._drv_lbaas = drv_base.LBaaSDriver.get_instance()
|
||||
self._l7_router = None
|
||||
self._l7_router_listeners = None
|
||||
|
||||
def on_present(self, route):
|
||||
if not self._l7_router or not self._l7_router_listeners:
|
||||
ing_ctl = ingress_ctl.IngressCtrlr.get_instance()
|
||||
self._l7_router, self._l7_router_listeners = (
|
||||
ing_ctl.get_router_and_listener())
|
||||
if not self._l7_router or not self._l7_router_listeners:
|
||||
LOG.info("No L7 router found - do nothing")
|
||||
return
|
||||
|
||||
route_spec = self._get_route_spec(route)
|
||||
if not route_spec:
|
||||
route_spec = obj_route.RouteSpec()
|
||||
|
||||
if self._should_ignore(route, route_spec):
|
||||
return
|
||||
route_state = self._get_route_state(route)
|
||||
if not route_state:
|
||||
route_state = obj_route.RouteState()
|
||||
|
||||
self._sync_router_pool(route, route_spec, route_state)
|
||||
self._sync_l7_policy(route, route_spec, route_state)
|
||||
self._sync_host_l7_rule(route, route_spec, route_state)
|
||||
self._sync_path_l7_rule(route, route_spec, route_state)
|
||||
|
||||
self._set_route_state(route, route_state)
|
||||
self._set_route_spec(route, route_spec)
|
||||
self._send_route_notification_to_ep(
|
||||
route, route_spec.to_service)
|
||||
|
||||
def _get_endpoints_link_by_route(self, route_link, ep_name):
|
||||
route_link = route_link.replace(
|
||||
ocp_const.OCP_API_BASE, k_const.K8S_API_BASE)
|
||||
link_parts = route_link.split('/')
|
||||
if link_parts[-2] != 'routes':
|
||||
raise k_exc.IntegrityError(_(
|
||||
"Unsupported route link: %(link)s") % {
|
||||
'link': route_link})
|
||||
link_parts[-2] = 'endpoints'
|
||||
link_parts[-1] = ep_name
|
||||
return "/".join(link_parts)
|
||||
|
||||
def _send_route_notification_to_ep(self, route, ep_name):
|
||||
route_link = route['metadata']['selfLink']
|
||||
ep_link = self._get_endpoints_link_by_route(route_link, ep_name)
|
||||
k8s = clients.get_kubernetes_client()
|
||||
try:
|
||||
k8s.get(ep_link)
|
||||
except k_exc.K8sClientException:
|
||||
LOG.debug("Failed to get EP link : %s", ep_link)
|
||||
return
|
||||
|
||||
route_notifier = obj_lbaas.LBaaSRouteNotifier()
|
||||
route_notifier.routes.append(
|
||||
obj_lbaas.LBaaSRouteNotifEntry(
|
||||
route_id=route['metadata']['uid'], msg='RouteChanged'))
|
||||
route_notifier.obj_reset_changes(recursive=True)
|
||||
LOG.debug("Setting LBaaSRouteNotifier annotation: %r", route_notifier)
|
||||
annotation = jsonutils.dumps(route_notifier.obj_to_primitive(),
|
||||
sort_keys=True)
|
||||
k8s.annotate(
|
||||
ep_link,
|
||||
{k_const.K8S_ANNOTATION_LBAAS_RT_NOTIF: annotation},
|
||||
resource_version=route['metadata']['resourceVersion'])
|
||||
|
||||
def _should_ignore(self, route, route_spec):
|
||||
spec = route['spec']
|
||||
return ((not self._l7_router)
|
||||
or
|
||||
((spec.get('host') == route_spec.host) and
|
||||
(spec.get('path') == route_spec.path) and
|
||||
(spec['to'].get('name') == route_spec.to_service)))
|
||||
|
||||
def on_deleted(self, route):
|
||||
if not self._l7_router:
|
||||
LOG.info("No L7 router found - do nothing")
|
||||
return
|
||||
|
||||
route_state = self._get_route_state(route)
|
||||
if not route_state:
|
||||
return
|
||||
# NOTE(yboaron): deleting l7policy deletes also l7rules
|
||||
if route_state.l7_policy:
|
||||
self._drv_lbaas.release_l7_policy(
|
||||
self._l7_router, route_state.l7_policy)
|
||||
|
||||
if route_state.router_pool:
|
||||
if self._drv_lbaas.is_pool_used_by_other_l7policies(
|
||||
route_state.l7_policy, route_state.router_pool):
|
||||
LOG.debug("Can't delete pool (pointed by another route)")
|
||||
else:
|
||||
self._drv_lbaas.release_pool(
|
||||
self._l7_router, route_state.router_pool)
|
||||
# no more routes pointing to this pool/ep - update ep
|
||||
spec = route['spec']
|
||||
self._send_route_notification_to_ep(
|
||||
route, spec['to'].get('name'))
|
||||
|
||||
def _sync_router_pool(self, route, route_spec, route_state):
|
||||
if route_state.router_pool:
|
||||
return
|
||||
|
||||
pool_name = self._drv_lbaas.get_loadbalancer_pool_name(
|
||||
self._l7_router, route['metadata']['namespace'],
|
||||
route['spec']['to']['name'])
|
||||
pool = self._drv_lbaas.get_pool_by_name(
|
||||
pool_name, self._l7_router.project_id)
|
||||
if not pool:
|
||||
pool = self._drv_lbaas.ensure_pool_attached_to_lb(
|
||||
self._l7_router, route['metadata']['namespace'],
|
||||
route['spec']['to']['name'], protocol='HTTP')
|
||||
|
||||
route_state.router_pool = pool
|
||||
route_spec.to_service = route['spec']['to']['name']
|
||||
|
||||
def _sync_l7_policy(self, route, route_spec, route_state):
|
||||
if route_state.l7_policy:
|
||||
return
|
||||
# TBD , take care of listener HTTPS
|
||||
listener = self._l7_router_listeners[k_const.KURYR_L7_ROUTER_HTTP_PORT]
|
||||
|
||||
route_state.l7_policy = self._drv_lbaas.ensure_l7_policy(
|
||||
route['metadata']['namespace'], route['metadata']['name'],
|
||||
self._l7_router, route_state.router_pool, listener.id)
|
||||
|
||||
def _sync_host_l7_rule(self, route, route_spec, route_state):
|
||||
if route_spec.host == route['spec']['host']:
|
||||
return
|
||||
if not route_spec.host:
|
||||
route_state.h_l7_rule = self._drv_lbaas.ensure_l7_rule(
|
||||
self._l7_router, route_state.l7_policy,
|
||||
'EQUAL_TO', 'HOST_NAME', route['spec']['host'])
|
||||
else:
|
||||
self._drv_lbaas.update_l7_rule(
|
||||
route_state.h_l7_rule, route['spec']['host'])
|
||||
route_state.h_l7_rule.value = route['spec']['host']
|
||||
|
||||
route_spec.host = route['spec']['host']
|
||||
|
||||
def _sync_path_l7_rule(self, route, route_spec, route_state):
|
||||
if route_spec.path == route['spec'].get('path'):
|
||||
return
|
||||
if not route_spec.path:
|
||||
route_state.p_l7_rule = self._drv_lbaas.ensure_l7_rule(
|
||||
self._l7_router, route_state.l7_policy,
|
||||
'EQUAL_TO', 'PATH', route['spec']['path'])
|
||||
else:
|
||||
if route['spec']['path']:
|
||||
self._drv_lbaas.update_l7_rule(
|
||||
route_state.p_l7_rule, route['spec']['path'])
|
||||
route_state.p_l7_rule.value = route['spec']['path']
|
||||
else:
|
||||
self._drv_lbaas.release_l7_rule(route_state.p_l7_rule)
|
||||
route_state.p_l7_rule = None
|
||||
|
||||
route_spec.path = route['spec']['path']
|
||||
|
||||
def _get_route_spec(self, route):
|
||||
try:
|
||||
annotations = route['metadata']['annotations']
|
||||
annotation = annotations[k_const.K8S_ANNOTATION_ROUTE_SPEC]
|
||||
except KeyError:
|
||||
return obj_route.RouteSpec()
|
||||
obj_dict = jsonutils.loads(annotation)
|
||||
obj = obj_route.RouteSpec.obj_from_primitive(obj_dict)
|
||||
LOG.debug("Got RouteSpec from annotation: %r", obj)
|
||||
return obj
|
||||
|
||||
def _set_route_spec(self, route, route_spec):
|
||||
if route_spec is None:
|
||||
LOG.debug("Removing RouteSpec annotation: %r", route_spec)
|
||||
annotation = None
|
||||
else:
|
||||
route_spec.obj_reset_changes(recursive=True)
|
||||
LOG.debug("Setting RouteSpec annotation: %r", route_spec)
|
||||
annotation = jsonutils.dumps(route_spec.obj_to_primitive(),
|
||||
sort_keys=True)
|
||||
k8s = clients.get_kubernetes_client()
|
||||
k8s.annotate(route['metadata']['selfLink'],
|
||||
{k_const.K8S_ANNOTATION_ROUTE_SPEC: annotation},
|
||||
resource_version=route['metadata']['resourceVersion'])
|
||||
|
||||
def _get_route_state(self, route):
|
||||
try:
|
||||
annotations = route['metadata']['annotations']
|
||||
annotation = annotations[k_const.K8S_ANNOTATION_ROUTE_STATE]
|
||||
except KeyError:
|
||||
return obj_route.RouteState()
|
||||
obj_dict = jsonutils.loads(annotation)
|
||||
obj = obj_route.RouteState.obj_from_primitive(obj_dict)
|
||||
LOG.debug("Got RouteState from annotation: %r", obj)
|
||||
return obj
|
||||
|
||||
def _set_route_state(self, route, route_state):
|
||||
if route_state is None:
|
||||
LOG.debug("Removing RouteState annotation: %r", route_state)
|
||||
annotation = None
|
||||
else:
|
||||
route_state.obj_reset_changes(recursive=True)
|
||||
LOG.debug("Setting RouteState annotation: %r", route_state)
|
||||
annotation = jsonutils.dumps(route_state.obj_to_primitive(),
|
||||
sort_keys=True)
|
||||
k8s = clients.get_kubernetes_client()
|
||||
k8s.annotate(route['metadata']['selfLink'],
|
||||
{k_const.K8S_ANNOTATION_ROUTE_STATE: annotation},
|
||||
resource_version=route['metadata']['resourceVersion'])
|
@ -0,0 +1,186 @@
|
||||
# Copyright (c) 2018 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 oslo_utils import uuidutils
|
||||
|
||||
from kuryr_kubernetes.controller.handlers import ingress_lbaas as h_ing_lbaas
|
||||
from kuryr_kubernetes.objects import lbaas as obj_lbaas
|
||||
from kuryr_kubernetes.tests.unit.controller.handlers import \
|
||||
test_lbaas as t_lbaas
|
||||
|
||||
|
||||
class TestIngressLoadBalancerHandler(t_lbaas.TestLoadBalancerHandler):
|
||||
|
||||
@mock.patch('kuryr_kubernetes.controller.drivers.base'
|
||||
'.LBaaSDriver.get_instance')
|
||||
def test_init(self, m_get_drv_lbaas):
|
||||
m_get_drv_lbaas.return_value = mock.sentinel.drv_lbaas
|
||||
|
||||
handler = h_ing_lbaas.IngressLoadBalancerHandler()
|
||||
|
||||
self.assertEqual(mock.sentinel.drv_lbaas, handler._drv_lbaas)
|
||||
|
||||
def test_on_present_no_ing_ctrlr(self):
|
||||
endpoints = mock.sentinel.endpoints
|
||||
|
||||
m_handler = mock.Mock(spec=h_ing_lbaas.IngressLoadBalancerHandler)
|
||||
m_handler._l7_router = None
|
||||
h_ing_lbaas.IngressLoadBalancerHandler.on_present(m_handler, endpoints)
|
||||
|
||||
m_handler._get_lbaas_spec.assert_not_called()
|
||||
m_handler._should_ignore.assert_not_called()
|
||||
|
||||
def test_should_ignore(self):
|
||||
endpoints = mock.sentinel.endpoints
|
||||
lbaas_spec = mock.sentinel.lbaas_spec
|
||||
|
||||
m_handler = mock.Mock(spec=h_ing_lbaas.IngressLoadBalancerHandler)
|
||||
m_handler._has_pods.return_value = False
|
||||
|
||||
ret = h_ing_lbaas.IngressLoadBalancerHandler._should_ignore(
|
||||
m_handler, endpoints, lbaas_spec)
|
||||
self.assertEqual(True, ret)
|
||||
|
||||
m_handler._has_pods.assert_called_once_with(endpoints)
|
||||
|
||||
def test_should_ignore_with_pods(self):
|
||||
endpoints = mock.sentinel.endpoints
|
||||
lbaas_spec = mock.sentinel.lbaas_spec
|
||||
|
||||
m_handler = mock.Mock(spec=h_ing_lbaas.IngressLoadBalancerHandler)
|
||||
m_handler._has_pods.return_value = True
|
||||
|
||||
ret = h_ing_lbaas.IngressLoadBalancerHandler._should_ignore(
|
||||
m_handler, endpoints, lbaas_spec)
|
||||
self.assertEqual(False, ret)
|
||||
|
||||
m_handler._has_pods.assert_called_once_with(endpoints)
|
||||
|
||||
def _generate_route_state(self, vip, targets, project_id, subnet_id):
|
||||
name = 'DUMMY_NAME'
|
||||
drv = t_lbaas.FakeLBaaSDriver()
|
||||
lb = drv.ensure_loadbalancer(
|
||||
name, project_id, subnet_id, vip, None, 'ClusterIP')
|
||||
pool = drv.ensure_pool_attached_to_lb(lb, 'namespace',
|
||||
'svc_name', 'HTTP')
|
||||
|
||||
members = {}
|
||||
for ip, (listen_port, target_port) in targets.items():
|
||||
members.setdefault((ip, listen_port, target_port),
|
||||
drv.ensure_member(lb, pool,
|
||||
subnet_id, ip,
|
||||
target_port, None, None))
|
||||
return obj_lbaas.LBaaSRouteState(
|
||||
pool=pool,
|
||||
members=list(members.values()))
|
||||
|
||||
def _sync_route_members_impl(self, m_get_drv_lbaas, m_get_drv_project,
|
||||
m_get_drv_subnets, subnet_id, project_id,
|
||||
endpoints, state, spec):
|
||||
m_drv_lbaas = mock.Mock(wraps=t_lbaas.FakeLBaaSDriver())
|
||||
m_drv_project = mock.Mock()
|
||||
m_drv_project.get_project.return_value = project_id
|
||||
m_drv_subnets = mock.Mock()
|
||||
m_drv_subnets.get_subnets.return_value = {
|
||||
subnet_id: mock.sentinel.subnet}
|
||||
m_get_drv_lbaas.return_value = m_drv_lbaas
|
||||
m_get_drv_project.return_value = m_drv_project
|
||||
m_get_drv_subnets.return_value = m_drv_subnets
|
||||
|
||||
handler = h_ing_lbaas.IngressLoadBalancerHandler()
|
||||
|
||||
handler._l7_router = t_lbaas.FakeLBaaSDriver().ensure_loadbalancer(
|
||||
name='L7_Router',
|
||||
project_id=project_id,
|
||||
subnet_id=subnet_id,
|
||||
ip='1.2.3.4',
|
||||
security_groups_ids=None,
|
||||
service_type='ClusterIP')
|
||||
|
||||
with mock.patch.object(handler, '_get_pod_subnet') as m_get_pod_subnet:
|
||||
m_get_pod_subnet.return_value = subnet_id
|
||||
handler._sync_lbaas_route_members(endpoints, state, spec)
|
||||
|
||||
observed_targets = sorted(
|
||||
(str(member.ip), (
|
||||
member.port,
|
||||
member.port))
|
||||
for member in state.members)
|
||||
return observed_targets
|
||||
|
||||
@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__sync_lbaas_route_members(self, m_get_drv_lbaas,
|
||||
m_get_drv_project, m_get_drv_subnets):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
subnet_id = uuidutils.generate_uuid()
|
||||
current_ip = '1.1.1.1'
|
||||
current_targets = {
|
||||
'1.1.1.101': (1001, 1001),
|
||||
'1.1.1.111': (1001, 1001),
|
||||
'1.1.1.201': (2001, 2001)}
|
||||
expected_ip = '2.2.2.2'
|
||||
expected_targets = {
|
||||
'2.2.2.101': (1201, 1201),
|
||||
'2.2.2.111': (1201, 1201),
|
||||
'2.2.2.201': (2201, 2201)}
|
||||
endpoints = self._generate_endpoints(expected_targets)
|
||||
state = self._generate_route_state(
|
||||
current_ip, current_targets, project_id, subnet_id)
|
||||
spec = self._generate_lbaas_spec(expected_ip, expected_targets,
|
||||
project_id, subnet_id)
|
||||
|
||||
observed_targets = self._sync_route_members_impl(
|
||||
m_get_drv_lbaas, m_get_drv_project, m_get_drv_subnets,
|
||||
subnet_id, project_id, endpoints, state, spec)
|
||||
|
||||
self.assertEqual(sorted(expected_targets.items()), observed_targets)
|
||||
|
||||
def test_on_deleted_no_ingress_controller(self):
|
||||
endpoints = mock.sentinel.endpoints
|
||||
m_handler = mock.Mock(spec=h_ing_lbaas.IngressLoadBalancerHandler)
|
||||
m_handler._l7_router = None
|
||||
h_ing_lbaas.IngressLoadBalancerHandler.on_deleted(m_handler, endpoints)
|
||||
|
||||
m_handler._get_lbaas_route_state.assert_not_called()
|
||||
m_handler._remove_unused_route_members.assert_not_called()
|
||||
|
||||
def test_on_deleted(self):
|
||||
endpoints = mock.sentinel.endpoints
|
||||
project_id = uuidutils.generate_uuid()
|
||||
subnet_id = uuidutils.generate_uuid()
|
||||
|
||||
m_handler = mock.Mock(spec=h_ing_lbaas.IngressLoadBalancerHandler)
|
||||
m_handler._l7_router = t_lbaas.FakeLBaaSDriver().ensure_loadbalancer(
|
||||
name='L7_Router',
|
||||
project_id=project_id,
|
||||
subnet_id=subnet_id,
|
||||
ip='1.2.3.4',
|
||||
security_groups_ids=None,
|
||||
service_type='ClusterIP')
|
||||
|
||||
m_handler._get_lbaas_route_state.return_value = (
|
||||
obj_lbaas.LBaaSRouteState())
|
||||
m_handler._remove_unused_route_members.return_value = True
|
||||
|
||||
h_ing_lbaas.IngressLoadBalancerHandler.on_deleted(m_handler, endpoints)
|
||||
|
||||
m_handler._get_lbaas_route_state.assert_called_once()
|
||||
m_handler._remove_unused_route_members.assert_called_once()
|
@ -428,7 +428,10 @@ class FakeLBaaSDriver(drv_base.LBaaSDriver):
|
||||
|
||||
def ensure_pool_attached_to_lb(self, loadbalancer, namespace,
|
||||
svc_name, protocol):
|
||||
pass
|
||||
return obj_lbaas.LBaaSPool(id=uuidutils.generate_uuid(),
|
||||
loadbalancer_id=loadbalancer.id,
|
||||
project_id=loadbalancer.project_id,
|
||||
protocol=protocol)
|
||||
|
||||
def get_pool_by_name(self, pool_name, project_id):
|
||||
pass
|
||||
|
@ -0,0 +1,403 @@
|
||||
# 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_kubernetes.controller.drivers import l7_router as d_l7_rtr
|
||||
from kuryr_kubernetes.objects import lbaas as obj_lbaas
|
||||
from kuryr_kubernetes.objects import route as obj_route
|
||||
from kuryr_kubernetes.platform.ocp.controller.handlers import route as h_route
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
import mock
|
||||
|
||||
|
||||
class TestOcpRouteHandler(test_base.TestCase):
|
||||
|
||||
@mock.patch('kuryr_kubernetes.controller.drivers.base'
|
||||
'.L7RouterDriver.get_instance')
|
||||
def test_init(self, m_get_drv_l7_router):
|
||||
m_get_drv_l7_router.return_value = mock.sentinel.drv_l7_router
|
||||
|
||||
handler = h_route.OcpRouteHandler()
|
||||
self.assertEqual(mock.sentinel.drv_l7_router, handler._drv_l7_router)
|
||||
self.assertIsNone(handler._l7_router)
|
||||
self.assertIsNone(handler._l7_router_listeners)
|
||||
|
||||
def test_on_present(self):
|
||||
route_event = mock.sentinel.route_event
|
||||
route_spec = mock.sentinel.route_spec
|
||||
route_state = mock.sentinel.route_state
|
||||
route_spec.to_service = mock.sentinel.to_service
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._get_route_spec.return_value = route_spec
|
||||
m_handler._should_ignore.return_value = False
|
||||
m_handler._get_route_state.return_value = route_state
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
|
||||
h_route.OcpRouteHandler.on_present(m_handler, route_event)
|
||||
m_handler._sync_router_pool.assert_called_once_with(
|
||||
route_event, route_spec, route_state)
|
||||
m_handler._sync_l7_policy.assert_called_once_with(
|
||||
route_event, route_spec, route_state)
|
||||
m_handler._sync_host_l7_rule.assert_called_once_with(
|
||||
route_event, route_spec, route_state)
|
||||
m_handler._sync_path_l7_rule.assert_called_once_with(
|
||||
route_event, route_spec, route_state)
|
||||
m_handler._set_route_state.assert_called_once_with(
|
||||
route_event, route_state)
|
||||
m_handler._set_route_spec.assert_called_once_with(
|
||||
route_event, route_spec)
|
||||
m_handler._send_route_notification_to_ep.assert_called_once_with(
|
||||
route_event, route_spec.to_service)
|
||||
|
||||
def test_on_present_no_change(self):
|
||||
route_event = mock.sentinel.route_event
|
||||
route_spec = mock.sentinel.route_spec
|
||||
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._get_route_spec.return_value = route_spec
|
||||
m_handler._should_ignore.return_value = True
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
|
||||
h_route.OcpRouteHandler.on_present(m_handler, route_event)
|
||||
m_handler._get_route_spec.assert_called_once_with(
|
||||
route_event)
|
||||
m_handler._sync_router_pool.assert_not_called()
|
||||
m_handler._sync_l7_policy.assert_not_called()
|
||||
m_handler._sync_host_l7_rule.assert_not_called()
|
||||
m_handler._sync_path_l7_rule.assert_not_called()
|
||||
m_handler._set_route_state.assert_not_called()
|
||||
m_handler._set_route_spec.assert_not_called()
|
||||
m_handler._send_route_notification_to_ep.assert_not_called()
|
||||
|
||||
def test_get_endpoints_link_by_route(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
route_link = '/oapi/v1/namespaces/default/routes/my_route'
|
||||
ep_name = 'my_endpoint'
|
||||
expected_ep_link = '/api/v1/namespaces/default/endpoints/my_endpoint'
|
||||
ret_ep_path = h_route.OcpRouteHandler._get_endpoints_link_by_route(
|
||||
m_handler, route_link, ep_name)
|
||||
|
||||
self.assertEqual(expected_ep_link, ret_ep_path)
|
||||
|
||||
def test_get_endpoints_link_by_route_error(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
route_link = '/oapi/v1/namespaces/default/routes/my_route'
|
||||
ep_name = 'wrong_endpoint'
|
||||
expected_ep_link = '/api/v1/namespaces/default/endpoints/my_endpoint'
|
||||
ret_ep_path = h_route.OcpRouteHandler._get_endpoints_link_by_route(
|
||||
m_handler, route_link, ep_name)
|
||||
|
||||
self.assertNotEqual(expected_ep_link, ret_ep_path)
|
||||
|
||||
def test_should_ignore_l7_router_not_exist(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = None
|
||||
route = {'spec': {
|
||||
'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
expected_result = True
|
||||
|
||||
ret_value = h_route.OcpRouteHandler._should_ignore(
|
||||
m_handler, route, route_spec)
|
||||
|
||||
self.assertEqual(ret_value, expected_result)
|
||||
|
||||
def test_should_ignore_l7_router_exist_no_change(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
route = {'spec': {
|
||||
'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
expected_result = True
|
||||
ret_value = h_route.OcpRouteHandler._should_ignore(
|
||||
m_handler, route, route_spec)
|
||||
self.assertEqual(ret_value, expected_result)
|
||||
|
||||
def test_should_ignore_l7_router_exist_with_changes(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
route = {'spec': {
|
||||
'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com1',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
expected_result = False
|
||||
ret_value = h_route.OcpRouteHandler._should_ignore(
|
||||
m_handler, route, route_spec)
|
||||
self.assertEqual(ret_value, expected_result)
|
||||
|
||||
def test_sync_router_pool_empty_pool(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
m_handler._drv_l7_router.ensure_pool.return_value = None
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace'},
|
||||
'spec': {'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com1',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
route_state = obj_route.RouteState()
|
||||
|
||||
h_route.OcpRouteHandler._sync_router_pool(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertIsNone(route_state.router_pool)
|
||||
|
||||
def test_sync_router_pool_valid_pool(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
ret_pool = obj_lbaas.LBaaSPool(
|
||||
name='TEST_NAME', project_id='TEST_PROJECT', protocol='TCP',
|
||||
listener_id='A57B7771-6050-4CA8-A63C-443493EC98AB',
|
||||
loadbalancer_id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C')
|
||||
|
||||
m_handler._drv_l7_router.ensure_pool.return_value = ret_pool
|
||||
route = {'metadata': {'namespace': 'namespace'},
|
||||
'spec': {'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com1',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
route_state = obj_route.RouteState()
|
||||
|
||||
h_route.OcpRouteHandler._sync_router_pool(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.router_pool, ret_pool)
|
||||
|
||||
def test_sync_l7_policy(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
listener = obj_lbaas.LBaaSListener(
|
||||
id='123443545',
|
||||
name='TEST_NAME', project_id='TEST_PROJECT', protocol='TCP',
|
||||
port=80, loadbalancer_id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C')
|
||||
m_handler._l7_router_listeners = {'80': listener}
|
||||
l7_policy = obj_route.RouteL7Policy(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44', name='myname',
|
||||
listener_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
redirect_pool_id='00EE9E11-91C2-41CF-8FD4-7970579E5C46',
|
||||
project_id='00EE9E11-91C2-41CF-8FD4-7970579E5C46')
|
||||
|
||||
route_state = obj_route.RouteState()
|
||||
m_handler._drv_l7_router.ensure_l7_policy.return_value = l7_policy
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com1',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
h_route.OcpRouteHandler._sync_l7_policy(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.l7_policy, l7_policy)
|
||||
|
||||
def test_sync_host_l7_rule_already_exist(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
h_l7_rule = obj_route.RouteL7Rule(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='HOST',
|
||||
value='www.example.com')
|
||||
|
||||
route_state = obj_route.RouteState(h_l7_rule=h_l7_rule)
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
h_route.OcpRouteHandler._sync_host_l7_rule(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.h_l7_rule, h_l7_rule)
|
||||
|
||||
def test_sync_host_l7_rule_new_host(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
h_l7_rule = obj_route.RouteL7Rule(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='HOST',
|
||||
value='www.example.com')
|
||||
|
||||
route_state = obj_route.RouteState(h_l7_rule=h_l7_rule)
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'new.www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
m_handler._drv_l7_router.ensure_l7_rule.return_value = h_l7_rule
|
||||
h_route.OcpRouteHandler._sync_host_l7_rule(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.h_l7_rule.value, route['spec']['host'])
|
||||
|
||||
def test_sync_path_l7_rule(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
|
||||
old_p_l7_rule = obj_route.RouteL7Rule(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='PATH',
|
||||
value='/nice_path/')
|
||||
|
||||
route_state = obj_route.RouteState(p_l7_rule=old_p_l7_rule)
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'new.www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='mypath',
|
||||
to_service='target_service')
|
||||
|
||||
ret_p_l7_rule = obj_route.RouteL7Rule(
|
||||
id='55559E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='55559E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='PATH',
|
||||
value='/nice_path/')
|
||||
|
||||
m_handler._drv_l7_router.ensure_l7_rule.return_value = ret_p_l7_rule
|
||||
h_route.OcpRouteHandler._sync_path_l7_rule(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.p_l7_rule, old_p_l7_rule)
|
||||
|
||||
def test_sync_path_l7_rule_route_spec_path_is_none(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
|
||||
old_p_l7_rule = obj_route.RouteL7Rule(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='PATH',
|
||||
value='/nice_path/')
|
||||
|
||||
route_state = obj_route.RouteState(p_l7_rule=old_p_l7_rule)
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'new.www.test.com', 'path': 'mypath',
|
||||
'to': {'name': 'target_service'}}}
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path=None,
|
||||
to_service='target_service')
|
||||
|
||||
ret_p_l7_rule = obj_route.RouteL7Rule(
|
||||
id='55559E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='55559E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='PATH',
|
||||
value='/nice_path/')
|
||||
|
||||
m_handler._drv_l7_router.ensure_l7_rule.return_value = ret_p_l7_rule
|
||||
h_route.OcpRouteHandler._sync_path_l7_rule(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.p_l7_rule, ret_p_l7_rule)
|
||||
|
||||
def test_sync_path_l7_rule_route_spec_not_sync(self):
|
||||
m_handler = mock.Mock(spec=h_route.OcpRouteHandler)
|
||||
m_handler._l7_router = obj_lbaas.LBaaSLoadBalancer(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C4C',
|
||||
project_id='TEST_PROJECT')
|
||||
m_handler._drv_l7_router = mock.Mock(
|
||||
spec=d_l7_rtr.LBaaSv2L7RouterDriver)
|
||||
|
||||
old_p_l7_rule = obj_route.RouteL7Rule(
|
||||
id='00EE9E11-91C2-41CF-8FD4-7970579E5C44',
|
||||
compare_type='EQUAL_TO',
|
||||
l7policy_id='00EE9E11-91C2-41CF-8FD4-7970579E5C45',
|
||||
type='PATH',
|
||||
value='/nice_path/')
|
||||
|
||||
route_state = obj_route.RouteState(p_l7_rule=old_p_l7_rule)
|
||||
|
||||
route = {'metadata': {'namespace': 'namespace', 'name': 'name'},
|
||||
'spec': {'host': 'new.www.test.com', 'path': 'new_path',
|
||||
'to': {'name': 'target_service'}}}
|
||||
|
||||
route_spec = obj_route.RouteSpec(
|
||||
host='www.test.com',
|
||||
path='path',
|
||||
to_service='target_service')
|
||||
m_handler._drv_l7_router.update_l7_rule.return_value = None
|
||||
|
||||
h_route.OcpRouteHandler._sync_path_l7_rule(
|
||||
m_handler, route, route_spec, route_state)
|
||||
self.assertEqual(route_state.p_l7_rule.value, route['spec']['path'])
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
An OpenShift route is a way to expose a service by giving it an
|
||||
externally-reachable hostname like www.example.com.
|
||||
A defined route and the endpoints identified by its service can be
|
||||
consumed by a router to provide named connectivity that allows external
|
||||
clients to reach your applications.
|
||||
Each route consists of a route name , target service details.
|
||||
To enable it the following handlers should be added :
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[kubernetes]
|
||||
enabled_handlers=vif,lb,lbaasspec,ingresslb,ocproute
|
@ -81,6 +81,8 @@ kuryr_kubernetes.controller.handlers =
|
||||
lbaasspec = kuryr_kubernetes.controller.handlers.lbaas:LBaaSSpecHandler
|
||||
lb = kuryr_kubernetes.controller.handlers.lbaas:LoadBalancerHandler
|
||||
namespace = kuryr_kubernetes.controller.handlers.namespace:NamespaceHandler
|
||||
ingresslb = kuryr_kubernetes.controller.handlers.ingress_lbaas:IngressLoadBalancerHandler
|
||||
ocproute = kuryr_kubernetes.platform.ocp.controller.handlers.route:OcpRouteHandler
|
||||
test_handler = kuryr_kubernetes.tests.unit.controller.handlers.test_fake_handler:TestHandler
|
||||
|
||||
[files]
|
||||
|
Loading…
Reference in New Issue
Block a user