77import shutil
88import subprocess
99
10- from junitparser import JUnitXml
10+ import yaml
1111
1212logger = logging .getLogger (__name__ )
1313
@@ -17,9 +17,15 @@ def _find_sonobuoy():
1717 result = shutil .which ('sonobuoy' )
1818 if result :
1919 return result
20+ logger .debug ('sonobuoy not in PATH, trying $HOME/.local/bin' )
2021 result = os .path .join (os .path .expanduser ('~' ), '.local' , 'bin' , 'sonobuoy' )
2122 if os .path .exists (result ):
2223 return result
24+ logger .debug ('sonobuoy executable not found; expect errors' )
25+
26+
27+ def _fmt_result (counter ):
28+ return ', ' .join (f"{ counter .get (key , 0 )} { key } " for key in ('passed' , 'failed' , 'skipped' ))
2329
2430
2531class SonobuoyHandler :
@@ -68,15 +74,14 @@ def _sonobuoy_status_result(self):
6874 json_data = json .loads (process .stdout )
6975 counter = Counter ()
7076 for entry in json_data ["plugins" ]:
71- logger .debug (f"plugin: { entry ['plugin' ]} :{ entry ['result-status' ] } " )
72- for result , count in entry ["result-counts" ].items ():
73- counter [result ] += count
77+ logger .debug (f"plugin { entry ['plugin' ]} : { _fmt_result ( entry ['result-counts' ]) } " )
78+ for key , value in entry ["result-counts" ].items ():
79+ counter [key ] += value
7480 return counter
7581
7682 def _eval_result (self , counter ):
7783 """evaluate test results and return return code"""
78- result_str = ', ' .join (f"{ counter [key ]} { key } " for key in ('passed' , 'failed' , 'skipped' ))
79- result_message = f"sonobuoy reports { result_str } "
84+ result_message = f"sonobuoy reports { _fmt_result (counter )} "
8085 if counter ['failed' ]:
8186 logger .error (result_message )
8287 return 3
@@ -90,37 +95,33 @@ def _preflight_check(self):
9095 if not self .sonobuoy :
9196 raise RuntimeError ("sonobuoy executable not found; is it in PATH?" )
9297
93- def _sonobuoy_retrieve_result (self ):
98+ def _sonobuoy_retrieve_result (self , plugin = 'e2e' ):
9499 """
95- This method invokes sonobuoy to store the results in a subdirectory of
96- the working directory. The Junit results file contained in it is then
97- analyzed in order to interpret the relevant information it containes
100+ Invoke sonobuoy to retrieve results and to store them in a subdirectory of
101+ the working directory. Analyze the results yaml file for given `plugin` and
102+ log each failure as ERROR. Return summary dict like `_sonobuoy_status_result`.
98103 """
99104 logger .debug (f"retrieving results to { self .result_dir_name } " )
100105 result_dir = os .path .join (self .working_directory , self .result_dir_name )
101- if os .path .exists (result_dir ):
102- raise Exception ("result directory already existing" )
103- os .mkdir (result_dir )
104-
105- # XXX use self._invoke_sonobuoy
106- os .system (
107- # ~ f"sonobuoy retrieve {result_dir} -x --filename='{result_dir}' --kubeconfig='{self.kubeconfig_path}'"
108- f"sonobuoy retrieve { result_dir } --kubeconfig='{ self .kubeconfig_path } '"
109- )
110- logger .debug (
111- f"parsing JUnit result from { result_dir + '/plugins/e2e/results/global/junit_01.xml' } "
112- )
113- xml = JUnitXml .fromfile (result_dir + "/plugins/e2e/results/global/junit_01.xml" )
106+ os .makedirs (result_dir , exist_ok = True )
107+
108+ self ._invoke_sonobuoy ("retrieve" , "-x" , result_dir )
109+ yaml_path = os .path .join (result_dir , 'plugins' , plugin , 'sonobuoy_results.yaml' )
110+ logger .debug (f"parsing results from { yaml_path } " )
111+ with open (yaml_path , "r" ) as fileobj :
112+ result_obj = yaml .load (fileobj .read (), yaml .SafeLoader )
114113 counter = Counter ()
115- for suite in xml :
116- for case in suite :
117- if case .is_passed is True : # XXX why `is True`???
118- counter ['passed' ] += 1
119- elif case .is_skipped is True :
120- counter ['skipped' ] += 1
121- else :
122- counter ['failed' ] += 1
123- logger .error (f"{ case .name } " )
114+ for item1 in result_obj .get ('items' , ()):
115+ # file ...
116+ for item2 in item1 .get ('items' , ()):
117+ # suite ...
118+ for item in item2 .get ('items' , ()):
119+ # testcase ... or so
120+ status = item .get ('status' , 'skipped' )
121+ counter [status ] += 1
122+ if status == 'failed' :
123+ logger .error (f"FAILED: { item ['name' ]} " ) # <-- this is why this method exists!
124+ logger .info (f"{ plugin } results: { _fmt_result (counter )} " )
124125 return counter
125126
126127 def run (self ):
@@ -133,11 +134,12 @@ def run(self):
133134 self ._sonobuoy_run ()
134135 return_code = self ._eval_result (self ._sonobuoy_status_result ())
135136 print (self .check_name + ": " + ("PASS" , "FAIL" )[min (1 , return_code )])
137+ try :
138+ self ._sonobuoy_retrieve_result ()
139+ except Exception :
140+ # swallow exception for the time being
141+ logger .debug ('problem retrieving results' , exc_info = True )
136142 return return_code
137-
138- # ERROR: currently disabled due to: "error retrieving results: unexpected EOF"
139- # might be related to following bug: https://github.com/vmware-tanzu/sonobuoy/issues/1633
140- # self._sonobuoy_retrieve_result(self)
141143 except BaseException :
142144 logger .exception ("something went wrong" )
143145 return 112
0 commit comments