8000 Merge pull request #50 from kubernetes-incubator/o6 · mbohlool/client-python@5677578 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5677578

Browse files
authored
Merge pull request kubernetes-client#50 from kubernetes-incubator/o6
Kube config loader improvement
2 parents 812125f + b888000 commit 5677578

File tree

7 files changed

+830
-167
lines changed

7 files changed

+830
-167
lines changed

examples/example4.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 import client, config
18+
from kubernetes.client import configuration
19+
20+
21+
def main():
22+
config_file = os.path.join(os.path.expanduser('~'), '.kube', 'config')
23+
contexts, active_context = config.list_kube_config_contexts(config_file)
24+
if not contexts:
25+
print("Cannot find any context in kube-config file.")
26+
return
27+
contexts = [context['name'] for context in contexts]
28+
active_context = active_context['name']
29+
for i, context in enumerate(contexts):
30+
format_str = "%d. %s"
31+
if context == active_context:
32+
format_str = "* " + format_str
33+
print(format_str % (i, context))
34+
context = input("Enter context number: ")
35+
context = int(context)
36+
if context not in range(len(contexts)):
37+
print(
38+
"Number out of range. Using default context %s." %
39+
active_context)
40+
context_name = active_context
41+
else:
42+
context_name = contexts[context]
43+
44+
# Configs can be set in Configuration class directly or using helper
45+
# utility
46+
config.load_kube_config(config_file, context_name)
47+
48+
print("Active host is %s" % configuration.host)
49+
50+
v1 = client.CoreV1Api()
51+
print("Listing pods with their IPs:")
52+
ret = v1.list_pod_for_all_namespaces(watch=False)
53+
for item in ret.items:
54+
print(
55+
"%s\t%s\t%s" %
56+
(item.status.pod_ip,
57+
item.metadata.namespace,
58+
item.metadata.name))
59+
60+
61+
if __name__ == '__main__':
62+
main()

kubernetes/config/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from .kube_config import load_kube_config
15+
from .config_exception import ConfigException
1616
from .incluster_config import load_incluster_config
17-
from .incluster_config import ConfigException
17+
from .kube_config import list_kube_config_contexts
18+
from .kube_config import load_kube_config

kubernetes/config/config_exception.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
16+
class ConfigException(Exception):
17+
pass

kubernetes/config/incluster_config.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
from kubernetes.client import configuration
1818

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"
19+
from .config_exception import ConfigException
20+
21+
SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST"
22+
SERVICE_PORT_ENV_NAME = "KUBERNETES_SERVICE_PORT"
23+
SERVICE_TOKEN_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/token"
24+
SERVICE_CERT_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
2325

2426

2527
def _join_host_port(host, port):
@@ -31,16 +33,10 @@ def _join_host_port(host, port):
3133
return template % (host, port)
3234

3335

34-
class ConfigException(Exception):
35-
pass
36-
37-
3836
class InClusterConfigLoader(object):
3937

40-
def __init__(self, host_env_name, port_env_name, token_filename,
38+
def __init__(self, token_filename,
4139
cert_filename, environ=os.environ):
42-
self._host_env_name = host_env_name
43-
self._port_env_name = port_env_name
4440
self._token_filename = token_filename
4541
self._cert_filename = cert_filename
4642
self._environ = environ
@@ -50,24 +46,34 @@ def load_and_set(self):
5046
self._set_config()
5147

5248
def _load_config(self):
53-
if (self._host_env_name not in self._environ or
54-
self._port_env_name not in self._environ):
49+
if (SERVICE_HOST_ENV_NAME not in self._environ or
50+
SERVICE_PORT_ENV_NAME not in self._environ):
5551
raise ConfigException("Service host/port is not set.")
5652

53+
if (not self._environ[SERVICE_HOST_ENV_NAME] or
54+
not self._environ[SERVICE_PORT_ENV_NAME]):
55+
raise ConfigException("Service host/port is set but empty.")
56+
5757
self.host = (
58-
"https://" + _join_host_port(self._environ[self._host_env_name],
59-
self._environ[self._port_env_name]))
58+
"https://" + _join_host_port(self._environ[SERVICE_HOST_ENV_NAME],
59+
self._environ[SERVICE_PORT_ENV_NAME]))
6060

6161
if not os.path.isfile(self._token_filename):
6262
raise ConfigException("Service token file does not exists.")
6363

6464
with open(self._token_filename) as f:
6565
self.token = f.read()
66+
if not self.token:
67+
raise ConfigException("Token file exists but empty.")
6668

6769
if not os.path.isfile(self._cert_filename):
6870
raise ConfigException(
6971
"Service certification file does not exists.")
7072

73+
with open(self._cert_filename) as f:
74+
if not f.read():
75+
raise ConfigException("Cert file exists but empty.")
76+
7177
self.ssl_ca_cert = self._cert_filename
7278

7379
def _set_config(self):
@@ -81,7 +87,5 @@ def load_incluster_config():
8187
cluster. It's intended for clients that expect to be running inside a pod
8288
running on kubernetes. It will raise an exception if called from a process
8389
not running in a kubernetes environment."""
84-
InClusterConfigLoader(host_env_name=_SERVICE_HOST_ENV_NAME,
85-
port_env_name=_SERVICE_PORT_ENV_NAME,
86-
token_filename=_SERVICE_TOKEN_FILENAME,
87-
cert_filename=_SERVICE_CERT_FILENAME).load_and_set()
90+
InClusterConfigLoader(token_filename=SERVICE_TOKEN_FILENAME,
91+
cert_filename=SERVICE_CERT_FILENAME).load_and_set()

kubernetes/config/incluster_config_test.py

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@
1616
import tempfile
1717
import unittest
1818

19-
from kubernetes.client import configuration
20-
21-
from .incluster_config import (_SERVICE_HOST_ENV_NAME, _SERVICE_PORT_ENV_NAME,
22-
ConfigException, InClusterConfigLoader)
19+
from .config_exception import ConfigException
20+
from .incluster_config import (SERVICE_HOST_ENV_NAME, SERVICE_PORT_ENV_NAME,
21+
InClusterConfigLoader, _join_host_port)
2322

2423
_TEST_TOKEN = "temp_token"
24+
_TEST_CERT = "temp_cert"
2525
_TEST_HOST = "127.0.0.1"
26-
_TEST_IPV6_HOST = "::1"
2726
_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}
27+
_TEST_HOST_PORT = "127.0.0.1:80"
28+
_TEST_IPV6_HOST = "::1"
29+
_TEST_IPV6_HOST_PORT = "[::1]:80"
30+
31+
_TEST_ENVIRON = {SERVICE_HOST_ENV_NAME: _TEST_HOST,
32+
SERVICE_PORT_ENV_NAME: _TEST_PORT}
33+
_TEST_IPV6_ENVIRON = {SERVICE_HOST_ENV_NAME: _TEST_IPV6_HOST,
34+
SERVICE_PORT_ENV_NAME: _TEST_PORT}
3235

3336

3437
class InClusterConfigTest(unittest.TestCase):
@@ -49,38 +52,29 @@ def _create_file_with_temp_content(self, content=""):
4952

5053
def get_test_loader(
5154
self,
52-
host_env_name=_SERVICE_HOST_ENV_NAME,
53-
port_env_name=_SERVICE_PORT_ENV_NAME,
5455
token_filename=None,
5556
cert_filename=None,
5657
environ=_TEST_ENVIRON):
5758
if not token_filename:
5859
token_filename = self._create_file_with_temp_content(_TEST_TOKEN)
5960
if not cert_filename:
60-
cert_filename = self._create_file_with_temp_content()
61+
cert_filename = self._create_file_with_temp_content(_TEST_CERT)
6162
return InClusterConfigLoader(
62-
host_env_name=host_env_name,
63-
port_env_name=port_env_name,
6463
token_filename=token_filename,
6564
cert_filename=cert_filename,
6665
environ=environ)
6766

67+
def test_join_host_port(self):
68+
self.assertEqual(_TEST_HOST_PORT,
69+
_join_host_port(_TEST_HOST, _TEST_PORT))
70+
self.assertEqual(_TEST_IPV6_HOST_PORT,
71+
_join_host_port(_TEST_IPV6_HOST, _TEST_PORT))
72+
6873
def test_load_config(self):
69-
cert_filename = self._create_file_with_temp_content()
74+
cert_filename = self._create_file_with_temp_content(_TEST_CERT)
7075
loader = self.get_test_loader(cert_filename=cert_filename)
7176
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)
77+
self.assertEqual("https://" + _TEST_HOST_PORT, loader.host)
8478
self.assertEqual(cert_filename, loader.ssl_ca_cert)
8579
self.assertEqual(_TEST_TOKEN, loader.token)
8680

@@ -93,21 +87,45 @@ def _should_fail_load(self, config_loader, reason):
9387
pass
9488

9589
def test_no_port(self):
96-
loader = self.get_test_loader(port_env_name="not_exists_port")
90+
loader = self.get_test_loader(
91+
environ={SERVICE_HOST_ENV_NAME: _TEST_HOST})
9792
self._should_fail_load(loader, "no port specified")
9893

94+
def test_empty_port(self):
95+
loader = self.get_test_loader(
96+
environ={SERVICE_HOST_ENV_NAME: _TEST_HOST,
97+
SERVICE_PORT_ENV_NAME: ""})
98+
self._should_fail_load(loader, "empty port specified")
99+
99100
def test_no_host(self):
100-
loader = self.get_test_loader(host_env_name="not_exists_host")
101+
loader = self.get_test_loader(
102+
environ={SERVICE_PORT_ENV_NAME: _TEST_PORT})
101103
self._should_fail_load(loader, "no host specified")
102104

105+
def test_empty_host(self):
106+
loader = self.get_test_loader(
107+
environ={SERVICE_HOST_ENV_NAME: "",
108+
SERVICE_PORT_ENV_NAME: _TEST_PORT})
109+
self._should_fail_load(loader, "empty host specified")
110+
103111
def test_no_cert_file(self):
104112
loader = self.get_test_loader(cert_filename="not_exists_file_1123")
105113
self._should_fail_load(loader, "cert file does not exists")
106114

115+
def test_empty_cert_file(self):
116+
loader = self.get_test_loader(
117+
cert_filename=self._create_file_with_temp_content())
118+
self._should_fail_load(loader, "empty cert file provided")
119+
107120
def test_no_token_file(self):
108121
loader = self.get_test_loader(token_filename="not_exists_file_1123")
109122
self._should_fail_load(loader, "token file does not exists")
110123

124+
def test_empty_token_file(self):
125+
loader = self.get_test_loader(
126+
token_filename=self._create_file_with_temp_content())
127+
self._should_fail_load(loader, "empty token file provided")
128+
111129

112130
if __name__ == '__main__':
113131
unittest.main()

0 commit comments

Comments
 (0)
0