rsyslog/tests/mmkubernetes_test_server.py
Nelson Yen 80e4ce1769 mmkubernetes fix for apiserver error handling
submit on behalf of @abwaheed
- Added graceful handling of apiserver errors with unexpected responses,
  i.e., anything other than 200, 404, or 429. Idea is that apiserver
  transient error state will recover. We don't want mmkubernetes to miss
  metadata resolution for containers that don't have cached metadata.
  During these transient error states, mmkubernetes will provide basic
  container file path based resolution of namespace and pod metadata for
  new pods whose metadata is not yet cached. After this error state
  recovers, mmkubernetes is expected to resume its metadata resolution as
  expected.
- Added a unit test case for apiserver return 500 with changes to mock server
-  Fixed existing unit test that was failing due to missing expected results file
-  Added mmkubernetes unit tests to testbench
2021-06-29 18:14:25 -07:00

166 lines
5.0 KiB
Python

# Used by the mmkubernetes tests
# This is a simple http server which responds to kubernetes api requests
# and responds with kubernetes api server responses
# added 2018-04-06 by richm, released under ASL 2.0
import os
import json
import sys
try:
from http.server import HTTPServer, BaseHTTPRequestHandler
except ImportError:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
ns_template = '''{{
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {{
"name": "{namespace_name}",
"selfLink": "/api/v1/namespaces/{namespace_name}",
"uid": "{namespace_name}-id",
"resourceVersion": "2988",
"creationTimestamp": "2018-04-09T21:56:39Z",
"labels": {{
"label.1.key":"label 1 value",
"label.2.key":"label 2 value",
"label.with.empty.value":""
}},
"annotations": {{
"k8s.io/description": "",
"k8s.io/display-name": "",
"k8s.io/node-selector": "",
"k8s.io/sa.scc.mcs": "s0:c9,c4",
"k8s.io/sa.scc.supplemental-groups": "1000080000/10000",
"k8s.io/sa.scc.uid-range": "1000080000/10000",
"quota.k8s.io/cluster-resource-override-enabled": "false"
}}
}},
"spec": {{
"finalizers": [
"openshift.io/origin",
"kubernetes"
]
}},
"status": {{
"phase": "Active"
}}
}}'''
pod_template = '''{{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {{
"name": "{pod_name}",
"generateName": "{pod_name}-prefix",
"namespace": "{namespace_name}",
"selfLink": "/api/v1/namespaces/{namespace_name}/pods/{pod_name}",
"uid": "{pod_name}-id",
"resourceVersion": "3486",
"creationTimestamp": "2018-04-09T21:56:39Z",
"labels": {{
"component": "{pod_name}-component",
"deployment": "{pod_name}-deployment",
"deploymentconfig": "{pod_name}-dc",
"custom.label": "{pod_name}-label-value",
"label.with.empty.value":""
}},
"annotations": {{
"k8s.io/deployment-config.latest-version": "1",
"k8s.io/deployment-config.name": "{pod_name}-dc",
"k8s.io/deployment.name": "{pod_name}-deployment",
"k8s.io/custom.name": "custom value",
"annotation.with.empty.value":""
}}
}},
"status": {{
"phase": "Running",
"hostIP": "172.18.4.32",
"podIP": "10.128.0.14",
"startTime": "2018-04-09T21:57:39Z"
}}
}}'''
err_template = '''{{
"kind": "Status",
"apiVersion": "v1",
"metadata": {{
}},
"status": "Failure",
"message": "{kind} \\\"{objectname}\\\" {err}",
"reason": "{reason}",
"details": {{
"name": "{objectname}",
"kind": "{kind}"
}},
"code": {code}
}}'''
is_busy = False
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
# "http://localhost:18443/api/v1/namespaces/namespace-name2"
# parse url - either /api/v1/namespaces/$ns_name
# or
# /api/v1/namespaces/$ns_name/pods/$pod_name
global is_busy
comps = self.path.split('/')
status = 400
if len(comps) >= 5 and comps[1] == 'api' and comps[2] == 'v1' and comps[3] == 'namespaces':
resp = None
hsh = {'namespace_name':comps[4],'objectname':comps[4],'kind':'namespace'}
if len(comps) == 5: # namespace
resp_template = ns_template
status = 200
elif len(comps) == 7 and comps[5] == 'pods': # pod
hsh['pod_name'] = comps[6]
hsh['kind'] = 'pods'
hsh['objectname'] = hsh['pod_name']
resp_template = pod_template
status = 200
else:
resp = '{{"error":"do not recognize {0}"}}'.format(self.path)
if hsh['objectname'].endswith('not-found'):
status = 404
hsh['reason'] = 'NotFound'
hsh['err'] = 'not found'
resp_template = err_template
elif hsh['objectname'].endswith('busy'):
is_busy = not is_busy
if is_busy:
status = 429
hsh['reason'] = 'Busy'
hsh['err'] = 'server is too busy'
resp_template = err_template
elif hsh['objectname'].endswith('error'):
status = 500
hsh['reason'] = 'Error'
hsh['err'] = 'server is failing'
resp_template = err_template
if not resp:
hsh['code'] = status
resp = resp_template.format(**hsh)
else:
resp = '{{"error":"do not recognize {0}"}}'.format(self.path)
if not status == 200:
self.log_error(resp)
self.send_response(status)
self.end_headers()
self.wfile.write(json.dumps(json.loads(resp), separators=(',',':')).encode())
port = int(sys.argv[1])
httpd = HTTPServer(('localhost', port), SimpleHTTPRequestHandler)
# write "started" to file named in argv[3]
with open(sys.argv[3], "w") as ff:
ff.write("started\n")
# write pid to file named in argv[2]
with open(sys.argv[2], "w") as ff:
ff.write('{0}\n'.format(os.getpid()))
httpd.serve_forever()