Merge "Add HTTPS support to K8s API healthchecks"

This commit is contained in:
Zuul 2018-04-19 13:22:00 +00:00 committed by Gerrit Code Review
commit ffc19f0df2
5 changed files with 37 additions and 26 deletions
kuryr_kubernetes
cni
controller/managers
k8s_client.py
tests/unit
cni
controller/managers

@ -11,14 +11,15 @@
# limitations under the License. # limitations under the License.
import os import os
import requests
from six.moves import http_client as httplib from six.moves import http_client as httplib
from flask import Flask from flask import Flask
from pyroute2 import IPDB from pyroute2 import IPDB
from kuryr.lib._i18n import _ from kuryr.lib._i18n import _
from kuryr_kubernetes import clients
from kuryr_kubernetes.cni import utils from kuryr_kubernetes.cni import utils
from kuryr_kubernetes import exceptions as exc
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -108,7 +109,7 @@ class CNIHealthServer(object):
def readiness_status(self): def readiness_status(self):
data = 'ok' data = 'ok'
k8s_conn, k8s_status = self.verify_k8s_connection() k8s_conn = self.verify_k8s_connection()
if not _has_cap(CAP_NET_ADMIN, EFFECTIVE_CAPS): if not _has_cap(CAP_NET_ADMIN, EFFECTIVE_CAPS):
error_message = 'NET_ADMIN capabilities not present.' error_message = 'NET_ADMIN capabilities not present.'
@ -117,7 +118,7 @@ class CNIHealthServer(object):
if not k8s_conn: if not k8s_conn:
error_message = 'Error when processing k8s healthz request.' error_message = 'Error when processing k8s healthz request.'
LOG.error(error_message) LOG.error(error_message)
return error_message, k8s_status, self.headers return error_message, httplib.INTERNAL_SERVER_ERROR, self.headers
LOG.info('CNI driver readiness verified.') LOG.info('CNI driver readiness verified.')
return data, httplib.OK, self.headers return data, httplib.OK, self.headers
@ -160,8 +161,10 @@ class CNIHealthServer(object):
raise raise
def verify_k8s_connection(self): def verify_k8s_connection(self):
path = '/healthz' k8s = clients.get_kubernetes_client()
address = CONF.kubernetes.api_root try:
url = address + path k8s.get('/healthz', json=False, headers={'Connection': 'close'})
resp = requests.get(url, headers={'Connection': 'close'}) except exc.K8sClientException:
return resp.content == 'ok', resp.status_code LOG.exception('Exception when trying to reach Kubernetes API.')
return False
return True

@ -12,18 +12,21 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
from six.moves import http_client as httplib
from flask import Flask from flask import Flask
from oslo_config import cfg
from oslo_log import log as logging
from keystoneauth1 import exceptions as k_exc from keystoneauth1 import exceptions as k_exc
from keystoneclient import client as keystone_client from keystoneclient import client as keystone_client
from kuryr.lib._i18n import _ from kuryr.lib._i18n import _
from kuryr.lib import config as kuryr_config from kuryr.lib import config as kuryr_config
from kuryr.lib import utils from kuryr.lib import utils
from kuryr_kubernetes import clients
from kuryr_kubernetes import exceptions as exc
from kuryr_kubernetes.handlers import health as h_health from kuryr_kubernetes.handlers import health as h_health
import os
from oslo_config import cfg
from oslo_log import log as logging
import requests
from six.moves import http_client as httplib
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
@ -65,11 +68,11 @@ class HealthServer(object):
LOG.error(error_message) LOG.error(error_message)
return error_message, httplib.NOT_FOUND, self.headers return error_message, httplib.NOT_FOUND, self.headers
k8s_conn, status = self.verify_k8s_connection() k8s_conn = self.verify_k8s_connection()
if not k8s_conn: if not k8s_conn:
error_message = 'Error when processing k8s healthz request.' error_message = 'Error when processing k8s healthz request.'
LOG.error(error_message) LOG.error(error_message)
return error_message, status, self.headers return error_message, httplib.INTERNAL_SERVER_ERROR, self.headers
try: try:
self.verify_keystone_connection() self.verify_keystone_connection()
except k_exc.http.HttpError as h_ex: except k_exc.http.HttpError as h_ex:
@ -109,11 +112,13 @@ class HealthServer(object):
raise raise
def verify_k8s_connection(self): def verify_k8s_connection(self):
path = '/healthz' k8s = clients.get_kubernetes_client()
address = CONF.kubernetes.api_root try:
url = address + path k8s.get('/healthz', json=False, headers={'Connection': 'close'})
resp = requests.get(url, headers={'Connection': 'close'}) except exc.K8sClientException:
return resp.content == 'ok', resp.status_code LOG.exception('Exception when trying to reach Kubernetes API.')
return False
return True
def verify_keystone_connection(self): def verify_keystone_connection(self):
conf_group = kuryr_config.neutron_group.name conf_group = kuryr_config.neutron_group.name

@ -65,18 +65,21 @@ class K8sClient(object):
else: else:
self.verify_server = ca_crt_file self.verify_server = ca_crt_file
def get(self, path): def get(self, path, json=True, headers=None):
LOG.debug("Get %(path)s", {'path': path}) LOG.debug("Get %(path)s", {'path': path})
url = self._base_url + path url = self._base_url + path
header = {} header = {}
if self.token: if self.token:
header.update({'Authorization': 'Bearer %s' % self.token}) header.update({'Authorization': 'Bearer %s' % self.token})
if headers:
header.update(headers)
response = requests.get(url, cert=self.cert, response = requests.get(url, cert=self.cert,
verify=self.verify_server, verify=self.verify_server,
headers=header) headers=header)
if not response.ok: if not response.ok:
raise exc.K8sClientException(response.text) raise exc.K8sClientException(response.text)
return response.json() result = response.json() if json else response.text
return result
def _get_url_and_header(self, path): def _get_url_and_header(self, path):
url = self._base_url + path url = self._base_url + path

@ -54,9 +54,9 @@ class TestCNIHealthServer(base.TestCase):
'verify_k8s_connection') 'verify_k8s_connection')
def test_readiness_status_k8s_error(self, m_verify_k8s_conn, cap_tester): def test_readiness_status_k8s_error(self, m_verify_k8s_conn, cap_tester):
cap_tester.return_value = True cap_tester.return_value = True
m_verify_k8s_conn.return_value = False, 503 m_verify_k8s_conn.return_value = False
resp = self.test_client.get('/ready') resp = self.test_client.get('/ready')
self.assertEqual(503, resp.status_code) self.assertEqual(500, resp.status_code)
@mock.patch('pyroute2.IPDB.release') @mock.patch('pyroute2.IPDB.release')
def test_liveness_status(self, m_ipdb): def test_liveness_status(self, m_ipdb):

@ -65,11 +65,11 @@ class TestHealthServer(base.TestCase):
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
def test_read_k8s_error(self, m_exist, m_verify_k8s_conn): def test_read_k8s_error(self, m_exist, m_verify_k8s_conn):
m_exist.return_value = True m_exist.return_value = True
m_verify_k8s_conn.return_value = False, 503 m_verify_k8s_conn.return_value = False
resp = self.test_client.get('/ready') resp = self.test_client.get('/ready')
m_verify_k8s_conn.assert_called_once() m_verify_k8s_conn.assert_called_once()
self.assertEqual(503, resp.status_code) self.assertEqual(500, resp.status_code)
@mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.'
'verify_keystone_connection') 'verify_keystone_connection')