kuryr-kubernetes/kuryr_kubernetes/cni/utils.py
Michał Dulko d5f5db7005 CNI: Use K8S_POD_UID passed from CRI
Recent versions of cri-o and containerd are passing K8S_POD_UID as a CNI
argument, alongside with K8S_POD_NAMESPACE and K8S_POD_NAME. As both
latter variables cannot be used to safely identify a pod in the API
(StatefulSet recreates pods with the same name), we were prone to race
conditions in the CNI code that we could only workaround. The end effect
was mostly IP conflict.

Now that the UID argument is passed, we're able to compare the UID from
the request with the one in the API to make sure we're wiring the
correct pod. This commit implements that by making sure to move the
check to the code actually waiting for the pod to appear in the
registry. In case of K8S_POD_UID missing from the CNI request, API call
to retrieve Pod is used as a fallback.

We also know that this check doesn't work for static pods, so CRD and
controller needed to be updated to include information if the pod is
static on the KuryrPort spec, so that we can skip the check for the
static pods without the need to fetch Pod from the API.

Closes-Bug: 1963677
Change-Id: I5ef6a8212c535e90dee049a579c1483644d56db8
2022-03-08 12:28:48 +01:00

112 lines
3.4 KiB
Python

# Copyright (c) 2017 NEC Technologies India Pvt Ltd.
# 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 functools
import time
from http import client as httplib
from oslo_log import log as logging
PROC_ONE_CGROUP_PATH = '/proc/1/cgroup'
CONTAINER_RUNTIME_CGROUP_IDS = (
'docker', # This is set by docker/moby
'libpod', # This is set by podman
)
LOG = logging.getLogger(__name__)
SUCCESSFUL_REQUEST_CODE = (httplib.NO_CONTENT, httplib.ACCEPTED)
def running_under_container_runtime(proc_one_cg_path=PROC_ONE_CGROUP_PATH):
"""Returns True iff the CNI process is under a known container runtime."""
with open(proc_one_cg_path, 'r') as cgroup_info:
proc_one_cg_info = cgroup_info.read()
return any(runtime in proc_one_cg_info for runtime in
CONTAINER_RUNTIME_CGROUP_IDS)
def any_vif_inactive(vifs):
"""Return True if there is at least one VIF that's not ACTIVE."""
return any(not vif.active for vif in vifs.values())
class CNIConfig(dict):
def __init__(self, cfg):
super(CNIConfig, self).__init__(cfg)
for k, v in self.items():
if not k.startswith('_'):
setattr(self, k, v)
class CNIArgs(object):
def __init__(self, value):
for item in value.split(';'):
k, v = item.split('=', 1)
if not k.startswith('_'):
setattr(self, k, v)
def __contains__(self, key):
return hasattr(self, key)
class CNIParameters(object):
def __init__(self, env, cfg=None):
for k, v in env.items():
if k.startswith('CNI_'):
setattr(self, k, v)
if cfg is None:
self.config = CNIConfig(env['config_kuryr'])
else:
self.config = cfg
self.args = CNIArgs(self.CNI_ARGS)
def __repr__(self):
return repr({key: value for key, value in self.__dict__.items() if
key.startswith('CNI_')})
def log_ipdb(func):
@functools.wraps(func)
def with_logging(*args, **kwargs):
try:
return func(*args, **kwargs)
except RuntimeError as e:
try:
LOG.error('Error when manipulating network interfaces')
LOG.error(e.debug['traceback'])
LOG.debug('Full debugging info: %s', e.debug)
except AttributeError:
pass
raise
return with_logging
def measure_time(command):
"""Measures CNI ADD/DEL resquest duration"""
def decorator(method):
def wrapper(obj, *args, **kwargs):
start_time = time.time()
result = method(obj, *args, **kwargs)
cni_request_error = (
result[1] not in SUCCESSFUL_REQUEST_CODE)
obj._update_metrics(
command, cni_request_error, time.time() - start_time)
return result
wrapper.__name__ = method.__name__
return wrapper
return decorator