Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1be6936

Browse files
author
Scott Lee
committed
Merge branch 'master' of https://github.com/kubernetes-client/python into releasedocs
2 parents 482b855 + 457ac54 commit 1be6936

File tree

7 files changed

+372
-8
lines changed

7 files changed

+372
-8
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
# v12.0.0a1-snapshot
1+
# v12.0.0b1
2+
3+
Kubernetes API Version: 1.16.14
4+
5+
**New Feature:**
6+
- Accept and use client certificates from authentication plugins [kubernetes-client/python-base#205](https://github.com/kubernetes-client/python-base/pull/205)
7+
8+
**Bug Fix:**
9+
- Return when object is None in FileOrData class [kubernetes-client/python-base#201](https://github.com/kubernetes-client/python-base/pull/201)
10+
11+
# v12.0.0a1
212

313
Kubernetes API Version: 1.16.14
414

@@ -44,7 +54,6 @@ Kubernetes API Version: 1.16.14
4454
- Adds the ability to load kubeconfig from a dictionary [kubernetes-client/python-base#195](https://github.com/kubernetes-client/python-base/pull/195)
4555
- Allow incluster to accept pass-in config [kubernetes-client/python-base#193](https://github.com/kubernetes-client/python-base/pull/193)
4656
- Set expiration on token of incluster config and reload the token if it expires [kubernetes-client/python-base#191](https://github.com/kubernetes-client/python-base/pull/191)
47-
- Accept and use client certificates from authentication plugins [kubernetes-client/python-base#205](https://github.com/kubernetes-client/python-base/pull/205)
4857

4958
**Bug Fix:**
5059

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,4 @@ This will cause a failure in non-exec/attach calls. If you reuse your api clien
174174
recreate it between api calls that use _stream_ and other api calls.
175175

176176
See more at [exec example](examples/pod_exec.py).
177+

examples/pod_portforward.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Copyright 2020 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+
Shows the functionality of portforward streaming using an nginx container.
17+
"""
18+
19+
import select
20+
import socket
21+
import time
22+
23+
import six.moves.urllib.request as urllib_request
24+
25+
from kubernetes import config
26+
from kubernetes.client import Configuration
27+
from kubernetes.client.api import core_v1_api
28+
from kubernetes.client.rest import ApiException
29+
from kubernetes.stream import portforward
30+
31+
##############################################################################
32+
# Kubernetes pod port forwarding works by directly providing a socket which
33+
# the python application uses to send and receive data on. This is in contrast
34+
# to the go client, which opens a local port that the go application then has
35+
# to open to get a socket to transmit data.
36+
#
37+
# This simplifies the python application, there is not a local port to worry
38+
# about if that port number is available. Nor does the python application have
39+
# to then deal with opening this local port. The socket used to transmit data
40+
# is immediately provided to the python application.
41+
#
42+
# Below also is an example of monkey patching the socket.create_connection
43+
# function so that DNS names of the following formats will access kubernetes
44+
# ports:
45+
#
46+
# <pod-name>.<namespace>.kubernetes
47+
# <pod-name>.pod.<namespace>.kubernetes
48+
# <service-name>.svc.<namespace>.kubernetes
49+
# <service-name>.service.<namespace>.kubernetes
50+
#
51+
# These DNS name can be used to interact with pod ports using python libraries,
52+
# such as urllib.request and http.client. For example:
53+
#
54+
# response = urllib.request.urlopen(
55+
# 'https://metrics-server.service.kube-system.kubernetes/'
56+
# )
57+
#
58+
##############################################################################
59+
60+
61+
def portforward_commands(api_instance):
62+
name = 'portforward-example'
63+
resp = None
64+
try:
65+
resp = api_instance.read_namespaced_pod(name=name,
66+
namespace='default')
67+
except ApiException as e:
68+
if e.status != 404:
69+
print("Unknown error: %s" % e)
70+
exit(1)
71+
72+
if not resp:
73+
print("Pod %s does not exist. Creating it..." % name)
74+
pod_manifest = {
75+
'apiVersion': 'v1',
76+
'kind': 'Pod',
77+
'metadata': {
78+
'name': name
79+
},
80+
'spec': {
81+
'containers': [{
82+
'image': 'nginx',
83+
'name': 'nginx',
84+
}]
85+
}
86+
}
87+
api_instance.create_namespaced_pod(body=pod_manifest,
88+
namespace='default')
89+
while True:
90+
resp = api_instance.read_namespaced_pod(name=name,
91+
namespace='default')
92+
if resp.status.phase != 'Pending':
93+
break
94+
time.sleep(1)
95+
print("Done.")
96+
97+
pf = portforward(
98+
api_instance.connect_get_namespaced_pod_portforward,
99+
name, 'default',
100+
ports='80',
101+
)
102+
http = pf.socket(80)
103+
http.setblocking(True)
104+
http.sendall(b'GET / HTTP/1.1\r\n')
105+
http.sendall(b'Host: 127.0.0.1\r\n')
106+
http.sendall(b'Connection: close\r\n')
107+
http.sendall(b'Accept: */*\r\n')
108+
http.sendall(b'\r\n')
109+
response = b''
110+
while True:
111+
select.select([http], [], [])
112+
data = http.recv(1024)
113+
if not data:
114+
break
115+
response += data
116+
http.close()
117+
print(response.decode('utf-8'))
118+
error = pf.error(80)
119+
if error is None:
120+
print("No port forward errors on port 80.")
121+
else:
122+
print("Port 80 has the following error: %s" % error)
123+
124+
# Monkey patch socket.create_connection which is used by http.client and
125+
# urllib.request. The same can be done with urllib3.util.connection.create_connection
126+
# if the "requests" package is used.
127+
socket_create_connection = socket.create_connection
128+
129+
def kubernetes_create_connection(address, *args, **kwargs):
130+
dns_name = address[0]
131+
if isinstance(dns_name, bytes):
132+
dns_name = dns_name.decode()
133+
dns_name = dns_name.split(".")
134+
if dns_name[-1] != 'kubernetes':
135+
return socket_create_connection(address, *args, **kwargs)
136+
if len(dns_name) not in (3, 4):
137+
raise RuntimeError("Unexpected kubernetes DNS name.")
138+
namespace = dns_name[-2]
139+
name = dns_name[0]
140+
port = address[1]
141+
if len(dns_name) == 4:
142+
if dns_name[1] in ('svc', 'service'):
143+
service = api_instance.read_namespaced_service(name, namespace)
144+
for service_port in service.spec.ports:
145+
if service_port.port == port:
146+
port = service_port.target_port
147+
break
148+
else:
149+
raise RuntimeError(
150+
"Unable to find service port: %s" % port)
151+
label_selector = []
152+
for key, value in service.spec.selector.items():
153+
label_selector.append("%s=%s" % (key, value))
154+
pods = api_instance.list_namespaced_pod(
155+
namespace, label_selector=",".join(label_selector)
156+
)
157+
if not pods.items:
158+
raise RuntimeError("Unable to find service pods.")
159+
name = pods.items[0].metadata.name
160+
if isinstance(port, str):
161+
for container in pods.items[0].spec.containers:
162+
for container_port in container.ports:
163+
if container_port.name == port:
164+
port = container_port.container_port
165+
break
166+
else:
167+
continue
168+
break
169+
else:
170+
raise RuntimeError(
171+
"Unable to find service port name: %s" % port)
172+
elif dns_name[1] != 'pod':
173+
raise RuntimeError(
174+
"Unsupported resource type: %s" %
175+
dns_name[1])
176+
pf = portforward(api_instance.connect_get_namespaced_pod_portforward,
177+
name, namespace, ports=str(port))
178+
return pf.socket(port)
179+
socket.create_connection = kubernetes_create_connection
180+
181+
# Access the nginx http server using the
182+
# "<pod-name>.pod.<namespace>.kubernetes" dns name.
183+
response = urllib_request.urlopen(
184+
'http://%s.pod.default.kubernetes' % name)
185+
html = response.read().decode('utf-8')
186+
response.close()
187+
print('Status Code: %s' % response.code)
188+
print(html)
189+
190+
191+
def main():
192+
config.load_kube_config()
193+
c = Configuration.get_default_copy()
194+
c.assert_hostname = False
195+
Configuration.set_default(c)
196+
core_v1 = core_v1_api.CoreV1Api()
197+
198+
portforward_commands(core_v1)
199+
200+
201+
if __name__ == '__main__':
202+
main()

kubernetes/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ configuration.api_key['authorization'] = 'YOUR_API_KEY'
6060
# Defining host is optional and default to http://localhost
6161
configuration.host = "http://localhost"
6262

63-
# Defining host is optional and default to http://localhost
64-
configuration.host = "http://localhost"
6563
# Enter a context with an instance of the API kubernetes.client
6664
with kubernetes.client.ApiClient(configuration) as api_client:
6765
# Create an instance of the API class

0 commit comments

Comments
 (0)