mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-15 10:30:40 +01:00
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
166 lines
5.0 KiB
Python
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()
|