8000 Merge pull request #32 from kubernetes-incubator/o4 · mbohlool/client-python@9506c55 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9506c55

Browse files
authored
Merge pull request kubernetes-client#32 from kubernetes-incubator/o4
Add in-cluster config support
2 parents 149d0ad + ba3e22a commit 9506c55

File tree

4 files changed

+202
-1
lines changed

4 files changed

+202
-1
lines changed

examples/example3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def main():
3434
name += "*"
3535
name += v.version
3636
versions.append(name)
37-
print("%-20s %s" % (api.name, ",".join(versions)))
37+
print("%-40s %s" % (api.name, ",".join(versions)))
3838

3939

4040
if __name__ == '__main__':

kubernetes/config/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
# limitations under the License.
1414

1515
from .kube_config import load_kube_config
16+
from .incluster_config import load_incluster_config
17+
from .incluster_config import ConfigException

kubernetes/config/incluster_config.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright 2016 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
17+
from kubernetes.client import configuration
18+
19+
_SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST"
20+
_SERVICE_PORT_ENV_NAME = "KUBERNETES_SERVICE_PORT"
21+
_SERVICE_TOKEN_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/token"
22+
_SERVICE_CERT_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
23+
24+
25+
def _join_host_port(host, port):
26+
"""Adapted golang's net.JoinHostPort"""
27+
template = "%s:%s"
28+
host_requires_bracketing = ':' in host or '%' in host
29+
if host_requires_bracketing:
30+
template = "[%s]:%s"
31+
return template % (host, port)
32+
33+
34+
class ConfigException(Exception):
35+
pass
36+
37+
38+
class InClusterConfigLoader(object):
39+
def __init__(self, host_env_name, port_env_name, token_filename,
40+
cert_filename, environ=os.environ):
41+
self._host_env_name = host_env_name
42+
self._port_env_name = port_env_name
43+
self._token_filename = token_filename
44+
self._cert_filename = cert_filename
45+
self._environ = environ
46+
47+
def load_and_set(self):
48+
self._load_config()
49+
self._set_config()
50+
51+
def _load_config(self):
52+
if (self._host_env_name not in self._environ or
53+
self._port_env_name not in self._environ):
54+
raise ConfigException("Service host/port is not set.")
55+
56+
self.host = (
57+
"https://" + _join_host_port(self._environ[self._host_env_name],
58+
67ED self._environ[self._port_env_name]))
59+
60+
if not os.path.isfile(self._token_filename):
61+
raise ConfigException("Service token file does not exists.")
62+
63+
with open(self._token_filename) as f:
64+
self.token = f.read()
65+
66+
if not os.path.isfile(self._cert_filename):
67+
raise ConfigException(
68+
"Service certification file does not exists.")
69+
70+
self.ssl_ca_cert = self._cert_filename
71+
72+
def _set_config(self):
73+
configuration.host = self.host
74+
configuration.ssl_ca_cert = self.ssl_ca_cert
75+
configuration.api_key['authorization'] = "bearer " + self.token
76+
77+
78+
def load_incluster_config():
79+
"""Use the service account kubernetes gives to pods to connect to kubernetes
80+
cluster. It's intended for clients that expect to be running inside a pod
81+
running on kubernetes. It will raise an exception if called from a process
82+
not running in a kubernetes environment."""
83+
InClusterConfigLoader(host_env_name=_SERVICE_HOST_ENV_NAME,
84+
port_env_name=_SERVICE_PORT_ENV_NAME,
85+
token_filename=_SERVICE_TOKEN_FILENAME,
86+
cert_filename=_SERVICE_CERT_FILENAME).load_and_set()
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Copyright 2016 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
import tempfile
17+
import unittest
18+
19+
from kubernetes.client import configuration
20+
21+
from .incluster_config import (ConfigException, InClusterConfigLoader,
22+
_SERVICE_HOST_ENV_NAME, _SERVICE_PORT_ENV_NAME)
23+
24+
_TEST_TOKEN = "temp_token"
25+
_TEST_HOST = "127.0.0.1"
26+
_TEST_IPV6_HOST = "::1"
27+
_TEST_PORT = "80"
28+
_TEST_ENVIRON = {_SERVICE_HOST_ENV_NAME: _TEST_HOST,
29+
_SERVICE_PORT_ENV_NAME: _TEST_PORT}
30+
_TEST_IPV6_ENVIRON = {_SERVICE_HOST_ENV_NAME: _TEST_IPV6_HOST,
31+
_SERVICE_PORT_ENV_NAME: _TEST_PORT}
32+
33+
34+
class InClusterConfigTest(unittest.TestCase):
35+
36+
def setUp(self):
37+
self._temp_files = []
38+
39+
def tearDown(self):
40+
for f in self._temp_files:
41+
os.remove(f)
42+
43+
def _create_file_with_temp_content(self, content=""):
44+
handler, name = tempfile.mkstemp()
45+
self._temp_files.append(name)
46+
os.write(handler, str.encode(content))
47+
os.close(handler)
48+
return name
49+
50+
def get_test_loader(
51+
self,
52+
host_env_name=_SERVICE_HOST_ENV_NAME,
53+
port_env_name=_SERVICE_PORT_ENV_NAME,
54+
token_filename=None,
55+
cert_filename=None,
56+
environ=_TEST_ENVIRON):
57+
if not token_filename:
58+
token_filename = self._create_file_with_temp_content(_TEST_TOKEN)
59+
if not cert_filename:
60+
cert_filename = self._create_file_with_temp_content()
61+
return InClusterConfigLoader(
62+
host_env_name=host_env_name,
63+
port_env_name=port_env_name,
64+
token_filename=token_filename,
65+
cert_filename=cert_filename,
66+
environ=environ)
67+
68+
def test_load_config(self):
69+
cert_filename = self._create_file_with_temp_content()
70+
loader = self.get_test_loader(cert_filename=cert_filename)
71+
loader._load_config()
72+
self.assertEqual("https://%s:%s" % (_TEST_HOST, _TEST_PORT),
73+
loader.host)
74+
self.assertEqual(cert_filename, loader.ssl_ca_cert)
75+
self.assertEqual(_TEST_TOKEN, loader.token)
76+
77+
def test_load_config_with_bracketed_hostname(self):
78+
cert_filename = self._create_file_with_temp_content()
79+
loader = self.get_test_loader(cert_filename=cert_filename,
80+
environ=_TEST_IPV6_ENVIRON)
81+
loader._load_config()
82+
self.assertEqual("https://[%s]:%s" % (_TEST_IPV6_HOST, _TEST_PORT),
83+
loader.host)
84+
self.assertEqual(cert_filename, loader.ssl_ca_cert)
85+
self.assertEqual(_TEST_TOKEN, loader.token)
86+
87+
def _should_fail_load(self, config_loader, reason):
88+
try:
89+
config_loader.load_and_set()
90+
self.fail("Should fail because %s" % reason)
91+
except ConfigException:
92+
# expected
93+
pass
94+
95+
def test_no_port(self):
96+
loader = self.get_test_loader(port_env_name="not_exists_port")
97+
self._should_fail_load(loader, "no port specified")
98+
99+
def test_no_host(self):
100+
loader = self.get_test_loader(host_env_name="not_exists_host")
101+
self._should_fail_load(loader, "no host specified")
102+
103+
def test_no_cert_file(self):
104+
loader = self.get_test_loader(cert_filename="not_exists_file_1123")
105+
self._should_fail_load(loader, "cert file does not exists")
106+
107+
def test_no_token_file(self):
108+
loader = self.get_test_loader(token_filename="not_exists_file_1123")
109+
self._should_fail_load(loader, "token file does not exists")
110+
111+
112+
if __name__ == '__main__':
113+
unittest.main()

0 commit comments

Comments
 (0)
0