k8s bearer token support

Co-Authored-By: Vikas Choudhary <vichoudh@redhat.com>
Change-Id: I9c8becf15c9fd5141a5f4468a04665fd09f8eaa6
Implements: blueprint token-auth-support
This commit is contained in:
Antoni Segura Puimedon 2017-05-10 11:55:57 -04:00
parent f9837df22c
commit 10b2e7d4cb
No known key found for this signature in database
GPG Key ID: B71BE48A9A349926
3 changed files with 82 additions and 17 deletions

View File

@ -43,6 +43,9 @@ k8s_opts = [
cfg.BoolOpt('ssl_verify_server_crt',
help=_("HTTPS K8S_API server identity verification"),
default=False),
cfg.StrOpt('token_file',
help=_("The token to talk to the k8s API"),
default=''),
cfg.StrOpt('pod_project_driver',
help=_("The driver to determine OpenStack project for pod ports"),
default='default'),

View File

@ -37,12 +37,24 @@ class K8sClient(object):
key_file = config.CONF.kubernetes.ssl_client_key_file
ca_crt_file = config.CONF.kubernetes.ssl_ca_crt_file
self.verify_server = config.CONF.kubernetes.ssl_verify_server_crt
if cert_file and not os.path.exists(cert_file):
raise RuntimeError(
_("Unable to find ssl cert_file : %s") % cert_file)
if key_file and not os.path.exists(key_file):
raise RuntimeError(
_("Unable to find ssl key_file : %s") % key_file)
token_file = config.CONF.kubernetes.token_file
self.token = None
self.cert = (None, None)
if token_file:
if os.path.exists(token_file):
with open(token_file, 'r') as f:
self.token = f.readline().rstrip('\n')
else:
raise RuntimeError(
_("Unable to find token_file : %s") % token_file)
else:
if cert_file and not os.path.exists(cert_file):
raise RuntimeError(
_("Unable to find ssl cert_file : %s") % cert_file)
if key_file and not os.path.exists(key_file):
raise RuntimeError(
_("Unable to find ssl key_file : %s") % key_file)
self.cert = (cert_file, key_file)
if self.verify_server:
if not ca_crt_file:
raise RuntimeError(
@ -53,13 +65,15 @@ class K8sClient(object):
else:
self.verify_server = ca_crt_file
self.cert = (cert_file, key_file)
def get(self, path):
LOG.debug("Get %(path)s", {'path': path})
url = self._base_url + path
header = {}
if self.token:
header.update({'Authorization': 'Bearer %s' % self.token})
response = requests.get(url, cert=self.cert,
verify=self.verify_server)
verify=self.verify_server,
headers=header)
if not response.ok:
raise exc.K8sClientException(response.text)
return response.json()
@ -75,6 +89,10 @@ 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})
while itertools.count(1):
data = jsonutils.dumps({
"metadata": {
@ -82,10 +100,8 @@ class K8sClient(object):
"resourceVersion": resource_version,
}
}, sort_keys=True)
response = requests.patch(url, data=data, headers={
'Content-Type': 'application/merge-patch+json',
'Accept': 'application/json',
}, cert=self.cert, verify=self.verify_server)
response = requests.patch(url, data=data, headers=header,
cert=self.cert, verify=self.verify_server)
if response.ok:
return response.json()['metadata']['annotations']
if response.status_code == requests.codes.conflict:
@ -109,12 +125,16 @@ class K8sClient(object):
def watch(self, path):
params = {'watch': 'true'}
url = self._base_url + path
header = {}
if self.token:
header.update({'Authorization': 'Bearer %s' % self.token})
# TODO(ivc): handle connection errors and retry on failure
while True:
with contextlib.closing(
requests.get(url, params=params, stream=True,
cert=self.cert, verify=self.verify_server)) as response:
cert=self.cert, verify=self.verify_server,
headers=header)) as response:
if not response.ok:
raise exc.K8sClientException(response.text)
for line in response.iter_lines(delimiter='\n'):

View File

@ -12,9 +12,10 @@
# 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 itertools
import mock
import os
import tempfile
from oslo_serialization import jsonutils
import requests
@ -30,8 +31,10 @@ class TestK8sClient(test_base.TestCase):
self.base_url = 'http://127.0.0.1:12345'
self.client = k8s_client.K8sClient(self.base_url)
default_cert = (None, None)
default_token = None
self.assertEqual(default_cert, self.client.cert)
self.assertEqual(False, self.client.verify_server)
self.assertEqual(default_token, self.client.token)
@mock.patch('os.path.exists')
@mock.patch('kuryr_kubernetes.config.CONF')
@ -39,6 +42,7 @@ class TestK8sClient(test_base.TestCase):
m_cfg.kubernetes.ssl_client_crt_file = 'dummy_crt_file_path'
m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path'
m_cfg.kubernetes.ssl_ca_crt_file = 'dummy_ca_file_path'
m_cfg.kubernetes.token_file = None
m_cfg.kubernetes.ssl_verify_server_crt = True
m_exist.return_value = True
test_client = k8s_client.K8sClient(self.base_url)
@ -50,6 +54,7 @@ class TestK8sClient(test_base.TestCase):
def test_https_client_init_invalid_client_crt_path(self, m_cfg):
m_cfg.kubernetes.ssl_client_crt_file = 'dummy_crt_file_path'
m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path'
m_cfg.kubernetes.token_file = None
self.assertRaises(RuntimeError, k8s_client.K8sClient, self.base_url)
@mock.patch('os.path.exists')
@ -59,9 +64,46 @@ class TestK8sClient(test_base.TestCase):
m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path'
m_cfg.kubernetes.ssl_ca_crt_file = None
m_cfg.kubernetes.ssl_verify_server_crt = True
m_cfg.kubernetes.token_file = None
m_exist.return_value = True
self.assertRaises(RuntimeError, k8s_client.K8sClient, self.base_url)
@mock.patch('requests.get')
@mock.patch('kuryr_kubernetes.config.CONF')
def test_bearer_token(self, m_cfg, m_get):
token_content = (
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3Nl"
"cnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc"
"3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bn"
"Qvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLWh4M3QxIiwia3ViZXJuZXRlcy5"
"pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQi"
"LCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51a"
"WQiOiIxYTkyM2ZmNi00MDkyLTExZTctOTMwYi1mYTE2M2VkY2ViMDUiLCJzdWIiOi"
"JzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.lzcPef"
"DQ-uzF5cD-5pLwTKpRvtvvxKB4LX8TLymrPLMTth8WGr1vT6jteJPmLiDZM2C5dZI"
"iFJpOw4LL1XLullik-ls-CmnTWq97NvlW1cZolC0mNyRz6JcL7gkH8WfUSjLA7x80"
"ORalanUxtl9-ghMGKCtKIACAgvr5gGT4iznGYQQRx_hKURs4O6Js5vhwNM6UuOKeW"
"GDDAlhgHMG0u59z3bhiBLl6jbQktZsu8c3diXniQb3sYqYQcGKUm1IQFujyA_ByDb"
"5GUtCv1BOPL_-IjYtvdJD8ZzQ_UnPFoYQklpDyJLB7_7qCGcfVEQbnSCh907NdKo4"
"w_8Wkn2y-Tg")
token_file = tempfile.NamedTemporaryFile(mode="w+t", delete=False)
try:
m_cfg.kubernetes.token_file = token_file.name
token_file.write(token_content)
token_file.close()
m_cfg.kubernetes.ssl_verify_server_crt = False
path = '/test'
client = k8s_client.K8sClient(self.base_url)
client.get(path)
headers = {
'Authorization': 'Bearer {}'.format(token_content)}
m_get.assert_called_once_with(
self.base_url + path, cert=(None, None), headers=headers,
verify=False)
finally:
os.unlink(m_cfg.kubernetes.token_file)
@mock.patch('requests.get')
def test_get(self, m_get):
path = '/test'
@ -74,7 +116,7 @@ class TestK8sClient(test_base.TestCase):
self.assertEqual(ret, self.client.get(path))
m_get.assert_called_once_with(self.base_url + path,
cert=(None, None), verify=False)
cert=(None, None), headers={}, verify=False)
@mock.patch('requests.get')
def test_get_exception(self, m_get):
@ -256,7 +298,7 @@ class TestK8sClient(test_base.TestCase):
self.assertEqual(cycles, m_get.call_count)
self.assertEqual(cycles, m_resp.close.call_count)
m_get.assert_called_with(self.base_url + path, stream=True,
m_get.assert_called_with(self.base_url + path, headers={}, stream=True,
params={'watch': 'true'}, cert=(None, None),
verify=False)