Skip to content

Commit 4135ede

Browse files
Auto-commit: sync changes
1 parent c330b84 commit 4135ede

40 files changed

Lines changed: 8015 additions & 45 deletions

kubernetes/base/watch/watch_test.py

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import unittest
1616

17+
import json
18+
1719
import os
1820

1921
import time
@@ -576,44 +578,43 @@ def test_pod_log_empty_lines(self):
576578
self.api.delete_namespaced_pod(name=pod_name, namespace=self.namespace)
577579
self.api.delete_namespaced_pod.assert_called_once_with(name=pod_name, namespace=self.namespace)
578580

579-
if __name__ == '__main__':
580-
def test_watch_with_deserialize_param(self):
581-
"""test watch.stream() deserialize param"""
582-
# prepare test data
583-
test_json = '{"type": "ADDED", "object": {"metadata": {"name": "test1", "resourceVersion": "1"}, "spec": {}, "status": {}}}'
584-
fake_resp = Mock()
585-
fake_resp.close = Mock()
586-
fake_resp.release_conn = Mock()
587-
fake_resp.stream = Mock(return_value=[test_json + '\n'])
588-
589-
fake_api = Mock()
590-
fake_api.get_namespaces = Mock(return_value=fake_resp)
591-
fake_api.get_namespaces.__doc__ = ':return: V1NamespaceList'
592-
593-
# test case with deserialize=True
594-
w = Watch()
595-
for e in w.stream(fake_api.get_namespaces, deserialize=True):
596-
self.assertEqual("ADDED", e['type'])
597-
# Verify that the object is deserialized correctly
598-
self.assertTrue(hasattr(e['object'], 'metadata'))
599-
self.assertEqual("test1", e['object'].metadata.name)
600-
self.assertEqual("1", e['object'].metadata.resource_version)
601-
# Verify that the original object is saved
602-
self.assertEqual(json.loads(test_json)['object'], e['raw_object'])
603-
604-
# test case with deserialize=False
605-
w = Watch()
606-
for e in w.stream(fake_api.get_namespaces, deserialize=False):
607-
self.assertEqual("ADDED", e['type'])
608-
# The validation object remains in the original dictionary format
609-
self.assertIsInstance(e['object'], dict)
610-
self.assertEqual("test1", e['object']['metadata']['name'])
611-
self.assertEqual("1", e['object']['metadata']['resourceVersion'])
612-
613-
# verify the api is called twice
614-
fake_api.get_namespaces.assert_has_calls([
615-
call(_preload_content=False, watch=True),
616-
call(_preload_content=False, watch=True)
617-
])
581+
def test_watch_with_deserialize_param(self):
582+
"""test watch.stream() deserialize param"""
583+
# prepare test data
584+
test_json = '{"type": "ADDED", "object": {"metadata": {"name": "test1", "resourceVersion": "1"}, "spec": {}, "status": {}}}'
585+
fake_resp = Mock()
586+
fake_resp.close = Mock()
587+
fake_resp.release_conn = Mock()
588+
fake_resp.stream = Mock(return_value=[test_json + '\n'])
589+
590+
fake_api = Mock()
591+
fake_api.get_namespaces = Mock(return_value=fake_resp)
592+
fake_api.get_namespaces.__doc__ = ':return: V1NamespaceList'
593+
594+
# test case with deserialize=True
595+
w = Watch()
596+
for e in w.stream(fake_api.get_namespaces, deserialize=True):
597+
self.assertEqual("ADDED", e['type'])
598+
# Verify that the object is deserialized correctly
599+
self.assertTrue(hasattr(e['object'], 'metadata'))
600+
self.assertEqual("test1", e['object'].metadata.name)
601+
self.assertEqual("1", e['object'].metadata.resource_version)
602+
# Verify that the original object is saved
603+
self.assertEqual(json.loads(test_json)['object'], e['raw_object'])
604+
605+
# test case with deserialize=False
606+
w = Watch()
607+
for e in w.stream(fake_api.get_namespaces, deserialize=False):
608+
self.assertEqual("ADDED", e['type'])
609+
# The validation object remains in the original dictionary format
610+
self.assertIsInstance(e['object'], dict)
611+
self.assertEqual("test1", e['object']['metadata']['name'])
612+
self.assertEqual("1", e['object']['metadata']['resourceVersion'])
613+
614+
# verify the api is called twice
615+
fake_api.get_namespaces.assert_has_calls([
616+
call(_preload_content=False, watch=True),
617+
call(_preload_content=False, watch=True)
618+
])
618619
if __name__ == '__main__':
619620
unittest.main()

kubernetes/config

Lines changed: 0 additions & 1 deletion
This file was deleted.

kubernetes/config/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
from os.path import exists, expanduser
16+
17+
from .config_exception import ConfigException
18+
from .incluster_config import load_incluster_config
19+
from .kube_config import (KUBE_CONFIG_DEFAULT_LOCATION,
20+
list_kube_config_contexts, load_kube_config,
21+
load_kube_config_from_dict, new_client_from_config, new_client_from_config_dict)
22+
23+
24+
def load_config(**kwargs):
25+
"""
26+
Wrapper function to load the kube_config.
27+
It will initially try to load_kube_config from provided path,
28+
then check if the KUBE_CONFIG_DEFAULT_LOCATION exists
29+
If neither exists, it will fall back to load_incluster_config
30+
and inform the user accordingly.
31+
32+
:param kwargs: A combination of all possible kwargs that
33+
can be passed to either load_kube_config or
34+
load_incluster_config functions.
35+
"""
36+
if "config_file" in kwargs.keys():
37+
load_kube_config(**kwargs)
38+
elif "kube_config_path" in kwargs.keys():
39+
kwargs["config_file"] = kwargs.pop("kube_config_path", None)
40+
load_kube_config(**kwargs)
41+
elif exists(expanduser(KUBE_CONFIG_DEFAULT_LOCATION)):
42+
load_kube_config(**kwargs)
43+
else:
44+
print(
45+
"kube_config_path not provided and "
46+
"default location ({0}) does not exist. "
47+
"Using inCluster Config. "
48+
"This might not work.".format(KUBE_CONFIG_DEFAULT_LOCATION))
49+
load_incluster_config(**kwargs)
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/dateutil.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright 2017 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 datetime
16+
import math
17+
import re
18+
19+
20+
class TimezoneInfo(datetime.tzinfo):
21+
def __init__(self, h, m):
22+
self._name = "UTC"
23+
if h != 0 and m != 0:
24+
self._name += "%+03d:%2d" % (h, m)
25+
self._delta = datetime.timedelta(hours=h, minutes=math.copysign(m, h))
26+
27+
def utcoffset(self, dt):
28+
return self._delta
29+
30+
def tzname(self, dt):
31+
return self._name
32+
33+
def dst(self, dt):
34+
return datetime.timedelta(0)
35+
36+
37+
UTC = TimezoneInfo(0, 0)
38+
39+
# ref https://www.ietf.org/rfc/rfc3339.txt
40+
_re_rfc3339 = re.compile(r"(\d\d\d\d)-(\d\d)-(\d\d)" # full-date
41+
r"[ Tt]" # Separator
42+
r"(\d\d):(\d\d):(\d\d)([.,]\d+)?" # partial-time
43+
r"([zZ ]|[-+]\d\d?:\d\d)?", # time-offset
44+
re.VERBOSE + re.IGNORECASE)
45+
_re_timezone = re.compile(r"([-+])(\d\d?):?(\d\d)?")
46+
47+
MICROSEC_PER_SEC = 1000000
48+
49+
50+
def parse_rfc3339(s):
51+
if isinstance(s, datetime.datetime):
52+
# no need to parse it, just make sure it has a timezone.
53+
if not s.tzinfo:
54+
return s.replace(tzinfo=UTC)
55+
return s
56+
groups = _re_rfc3339.search(s).groups()
57+
dt = [0] * 7
58+
for x in range(6):
59+
dt[x] = int(groups[x])
60+
us = 0
61+
if groups[6] is not None:
62+
partial_sec = float(groups[6].replace(",", "."))
63+
us = int(MICROSEC_PER_SEC * partial_sec)
64+
tz = UTC
65+
if groups[7] is not None and groups[7] != 'Z' and groups[7] != 'z':
66+
tz_groups = _re_timezone.search(groups[7]).groups()
67+
hour = int(tz_groups[1])
68+
minute = 0
69+
if tz_groups[0] == "-":
70+
hour *= -1
71+
if tz_groups[2]:
72+
minute = int(tz_groups[2])
73+
tz = TimezoneInfo(hour, minute)
74+
return datetime.datetime(
75+
year=dt[0], month=dt[1], day=dt[2],
76+
hour=dt[3], minute=dt[4], second=dt[5],
77+
microsecond=us, tzinfo=tz)
78+
79+
80+
def format_rfc3339(date_time):
81+
if date_time.tzinfo is None:
82+
date_time = date_time.replace(tzinfo=UTC)
83+
date_time = date_time.astimezone(UTC)
84+
return date_time.strftime('%Y-%m-%dT%H:%M:%SZ')

kubernetes/config/dateutil_test.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 unittest
16+
from datetime import datetime
17+
18+
from .dateutil import UTC, TimezoneInfo, format_rfc3339, parse_rfc3339
19+
20+
21+
class DateUtilTest(unittest.TestCase):
22+
23+
def _parse_rfc3339_test(self, st, y, m, d, h, mn, s, us):
24+
actual = parse_rfc3339(st)
25+
expected = datetime(y, m, d, h, mn, s, us, UTC)
26+
self.assertEqual(expected, actual)
27+
28+
def test_parse_rfc3339(self):
29+
self._parse_rfc3339_test("2017-07-25T04:44:21Z",
30+
2017, 7, 25, 4, 44, 21, 0)
31+
self._parse_rfc3339_test("2017-07-25 04:44:21Z",
32+
2017, 7, 25, 4, 44, 21, 0)
33+
self._parse_rfc3339_test("2017-07-25T04:44:21",
34+
2017, 7, 25, 4, 44, 21, 0)
35+
self._parse_rfc3339_test("2017-07-25T04:44:21z",
36+
2017, 7, 25, 4, 44, 21, 0)
37+
self._parse_rfc3339_test("2017-07-25T04:44:21+03:00",
38+
2017, 7, 25, 1, 44, 21, 0)
39+
self._parse_rfc3339_test("2017-07-25T04:44:21-03:00",
40+
2017, 7, 25, 7, 44, 21, 0)
41+
42+
self._parse_rfc3339_test("2017-07-25T04:44:21,005Z",
43+
2017, 7, 25, 4, 44, 21, 5000)
44+
self._parse_rfc3339_test("2017-07-25T04:44:21.005Z",
45+
2017, 7, 25, 4, 44, 21, 5000)
46+
self._parse_rfc3339_test("2017-07-25 04:44:21.0050Z",
47+
2017, 7, 25, 4, 44, 21, 5000)
48+
self._parse_rfc3339_test("2017-07-25T04:44:21.5",
49+
2017, 7, 25, 4, 44, 21, 500000)
50+
self._parse_rfc3339_test("2017-07-25T04:44:21.005z",
51+
2017, 7, 25, 4, 44, 21, 5000)
52+
self._parse_rfc3339_test("2017-07-25T04:44:21.005+03:00",
53+
2017, 7, 25, 1, 44, 21, 5000)
54+
self._parse_rfc3339_test("2017-07-25T04:44:21.005-03:00",
55+
2017, 7, 25, 7, 44, 21, 5000)
56+
57+
def test_format_rfc3339(self):
58+
self.assertEqual(
59+
format_rfc3339(datetime(2017, 7, 25, 4, 44, 21, 0, UTC)),
60+
"2017-07-25T04:44:21Z")
61+
self.assertEqual(
62+
format_rfc3339(datetime(2017, 7, 25, 4, 44, 21, 0,
63+
TimezoneInfo(2, 0))),
64+
"2017-07-25T02:44:21Z")
65+
self.assertEqual(
66+
format_rfc3339(datetime(2017, 7, 25, 4, 44, 21, 0,
67+
TimezoneInfo(-2, 30))),
68+
"2017-07-25T07:14:21Z")

0 commit comments

Comments
 (0)