Skip to content
This repository was archived by the owner on Oct 3, 2020. It is now read-only.

Commit 0899a31

Browse files
twz123hjacobs
authored andcommitted
Skip non-listable API groups (#42)
Listing resources inside API groups may fail, especially if they're aggregated and the downstream service is faulty. Handle and log exceptions during resource collection instead of failing the whole clean-up loop.
1 parent a2b65e7 commit 0899a31

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

kube_janitor/resources.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ def discover_namespaced_api_resources(api):
2626
r = api.get(version='/apis')
2727
r.raise_for_status()
2828
for group in r.json()['groups']:
29-
pref_version = group['preferredVersion']['groupVersion']
30-
logger.debug(f'Collecting resources for {pref_version}..')
31-
r2 = api.get(version=pref_version)
32-
r2.raise_for_status()
33-
for resource in r2.json()['resources']:
34-
if resource['namespaced'] and 'delete' in resource['verbs'] and '/' not in resource['name']:
35-
yield pref_version, resource
29+
try:
30+
pref_version = group['preferredVersion']['groupVersion']
31+
logger.debug(f'Collecting resources in API group {pref_version}..')
32+
r2 = api.get(version=pref_version)
33+
r2.raise_for_status()
34+
for resource in r2.json()['resources']:
35+
if resource['namespaced'] and 'delete' in resource['verbs'] and '/' not in resource['name']:
36+
yield pref_version, resource
37+
except Exception as e:
38+
logger.error(f'Could not collect resources in API group {pref_version}: {e}')
3639

3740

3841
def get_namespaced_resource_types(api):

tests/test_clean_up.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,44 @@ def get(**kwargs):
9494
assert counter['resources-processed'] == 1
9595

9696

97+
def test_ignore_nonlistable_api_group():
98+
api_mock = MagicMock(spec=NamespacedAPIObject, name='APIMock')
99+
100+
def get(**kwargs):
101+
if kwargs.get('url') == 'namespaces':
102+
data = {'items': [{'metadata': {'name': 'ns-1'}}]}
103+
elif kwargs.get('url') == 'customfoos':
104+
data = {'items': [{'metadata': {
105+
'name': 'foo-1',
106+
'namespace': 'ns-1',
107+
'creationTimestamp': '2019-01-17T15:14:38Z',
108+
# invalid TTL (no unit suffix)
109+
'annotations': {'janitor/ttl': '123'}}}]}
110+
elif kwargs['version'] == 'v1':
111+
data = {'resources': []}
112+
elif kwargs['version'] == 'srcco.de/v1':
113+
data = {'resources': [{'kind': 'CustomFoo', 'name': 'customfoos', 'namespaced': True, 'verbs': ['delete']}]}
114+
elif kwargs['version'] == 'kaput.srcco.de/v1':
115+
raise Exception('Catch me if you can!')
116+
elif kwargs['version'] == '/apis':
117+
data = {'groups': [
118+
{'preferredVersion': {'groupVersion': 'kaput.srcco.de/v1'}},
119+
{'preferredVersion': {'groupVersion': 'srcco.de/v1'}},
120+
]}
121+
else:
122+
data = {}
123+
response = MagicMock()
124+
response.json.return_value = data
125+
return response
126+
127+
api_mock.get = get
128+
counter = clean_up(api_mock, ALL, [], ALL, [], [], None, dry_run=False)
129+
assert counter['resources-processed'] == 2
130+
assert counter['customfoos-with-ttl'] == 0
131+
assert counter['customfoos-deleted'] == 0
132+
assert not api_mock.delete.called
133+
134+
97135
def test_ignore_invalid_ttl():
98136
api_mock = MagicMock(spec=NamespacedAPIObject, name='APIMock')
99137

0 commit comments

Comments
 (0)