61
61
import sys
62
62
import arrow
63
63
64
+ from io import StringIO
65
+
64
66
from blackduck import Client
65
67
from pprint import pprint ,pformat
66
68
@@ -75,12 +77,48 @@ def __init__(self, args):
75
77
with open (args .token_file , 'r' ) as tf :
76
78
self .access_token = tf .readline ().strip ()
77
79
self .no_verify = args .no_verify
80
+ self .reprocess_run_file = args .reprocess_run_file
78
81
self .connect ()
79
- self .init_project_data (args )
82
+ if self .reprocess_run_file :
83
+ self .load_project_data ()
84
+ else :
85
+ self .init_project_data (args )
80
86
81
87
def connect (self ):
82
88
self .client = Client (base_url = self .base_url , token = self .access_token , verify = self .no_verify , timeout = 60.0 , retries = 4 )
83
-
89
+
90
+ def load_project_data (self ):
91
+ with open (self .reprocess_run_file , "r" ) as f :
92
+ data = json .load (f )
93
+ self .project_data = data
94
+ self .project_data .pop ('log' , None )
95
+ discard = list ()
96
+ for name , subproject in self .project_data ['subprojects' ].items ():
97
+ if not self .has_errors (subproject ):
98
+ discard .append (name )
99
+ else :
100
+ self .project_data ['subprojects' ][name ].pop ('log' , None )
101
+ self .project_data ['subprojects' ][name ].pop ('status' , None )
102
+ self .project_data ['subprojects' ][name ].pop ('scan_results' ,None )
103
+ for name in discard :
104
+ del self .project_data ['subprojects' ][name ]
105
+
106
+ def has_errors (self , subproject ):
107
+ structure = False
108
+ runtime = False
109
+ if subproject ['status' ] != 'PRESENT' :
110
+ structure = True
111
+ if not subproject .get ('scan_results' , None ):
112
+ runtime = True
113
+ else :
114
+ rcodes = [r ['scan_results' ]['returncode' ] for r in subproject ['scan_results' ] if r .get ('scan_results' , None )]
115
+ if sum (rcodes ) > 0 :
116
+ runtime = True
117
+ if structure or runtime :
118
+ return True
119
+ else :
120
+ return False
121
+
84
122
def init_project_data (self ,args ):
85
123
self .project_data = dict ()
86
124
self .project_data ['project_name' ] = args .project_name
@@ -435,15 +473,51 @@ def proceed(self):
435
473
self .validate_project_structure ()
436
474
self .scan_container_images ()
437
475
476
+ def write_failure_report (data , output_file_name ):
477
+ s = StringIO ()
478
+ subprojects = data ['subprojects' ]
479
+ for subproject_name , subproject in subprojects .items ():
480
+ structure = False
481
+ runtime = False
482
+ if subproject ['status' ] != 'PRESENT' :
483
+ structure = True
484
+ if not subproject .get ('scan_results' , None ):
485
+ runtime = True
486
+ else :
487
+ rcodes = [r ['scan_results' ]['returncode' ] for r in subproject ['scan_results' ] if r .get ('scan_results' , None )]
488
+ if sum (rcodes ) > 0 :
489
+ runtime = True
490
+ if structure or runtime :
491
+ print (f"\n Status for { subproject ['project_name' ]} { subproject ['version_name' ]} " , file = s )
492
+ print (f"\t Structural failures present { structure } " , file = s )
493
+ print (f"\t Runtime failures present { runtime } \n " , file = s )
494
+
495
+ if subproject ['status' ] != 'PRESENT' :
496
+ for line in subproject ['log' ]:
497
+ print ('\t ' , line , file = s )
498
+ scan_results = subproject .get ('scan_results' ,[])
499
+ if len (scan_results ) == 0 :
500
+ print ("No scans were performed" , file = s )
501
+ else :
502
+ for invocation in scan_results :
503
+ returncode = invocation ['scan_results' ]['returncode' ]
504
+ if returncode > 0 :
505
+ print (f"\n \t Scan for { invocation ['name' ]} failed with returncode { returncode } \n " , file = s )
506
+ stdout = invocation ['scan_results' ]['stdout' ].split ('\n ' )
507
+ for line in stdout :
508
+ if 'ERROR' in line and 'certificates' not in line :
509
+ print ('\t ' , line , file = s )
510
+ with open (output_file_name , "w" ) as f :
511
+ f .write (s .getvalue ())
438
512
439
513
def parse_command_args ():
440
514
441
515
parser = argparse .ArgumentParser (description = program_description , formatter_class = argparse .RawTextHelpFormatter )
442
516
parser .add_argument ("-u" , "--base-url" , required = True , help = "Hub server URL e.g. https://your.blackduck.url" )
443
517
parser .add_argument ("-t" , "--token-file" , required = True , help = "File containing access token" )
444
518
parser .add_argument ("-pg" , "--project_group" , required = False , default = 'Multi-Image' , help = "Project Group to be used" )
445
- parser .add_argument ("-p" , "--project-name" , required = True , help = "Project Name" )
446
- parser .add_argument ("-pv" , "--version-name" , required = True , help = "Project Version Name" )
519
+ parser .add_argument ("-p" , "--project-name" , required = False , help = "Project Name" )
520
+ parser .add_argument ("-pv" , "--version-name" , required = False , help = "Project Version Name" )
447
521
group = parser .add_mutually_exclusive_group ()
448
522
group .add_argument ("-sp" , "--subproject-list" , required = False , help = "List of subprojects to generate with subproject:container:tag" )
449
523
group .add_argument ("-ssf" , "--subproject-spec-file" , required = False , help = "Excel or txt file containing subproject specification" )
@@ -456,7 +530,13 @@ def parse_command_args():
456
530
parser .add_argument ("--strict" , action = 'store_true' , help = "Fail if existing (sub)project versions already exist" )
457
531
parser .add_argument ("--binary" , action = 'store_true' , help = "Use binary scan for analysis" )
458
532
parser .add_argument ("-ifm" , "--individual-file-matching" , action = 'store_true' , help = "Turn Individual file matching on" )
459
- return parser .parse_args ()
533
+ parser .add_argument ("--reprocess-run-file" , help = "Reprocess Failures from previous run report." )
534
+ args = parser .parse_args ()
535
+ if not args .reprocess_run_file and not (args .project_name and args .version_name ):
536
+ parser .error ("[ -p/--project-name and -pv/--version-name ] or --reprocess-run-file are required" )
537
+ if args .reprocess_run_file and (args .project_name or args .version_name ):
538
+ parser .error ("[ -p/--project-name and -pv/--version-name ] or --reprocess-run-file are required" )
539
+ return args
460
540
461
541
def main ():
462
542
from datetime import datetime
@@ -467,28 +547,14 @@ def main():
467
547
mipm .proceed ()
468
548
469
549
if not args .remove :
470
- filename_complete = f"{ args .project_name } -{ args .version_name } -{ timestamp } -full.json"
471
- filename_failures = f"{ args .project_name } -{ args .version_name } -{ timestamp } -failures.json"
550
+ filename_base = f"{ mipm .project_data ['project_name' ]} -{ mipm .project_data ['version_name' ]} "
551
+ filename_complete = f"{ filename_base } -{ timestamp } -full.json"
552
+ filename_failure_report = f"{ filename_base } -{ timestamp } -failures.txt"
472
553
# write full processing log
473
554
with open (filename_complete , "w" ) as f :
474
555
json .dump (mipm .project_data , f , indent = 2 )
475
556
476
- failures = list ()
477
- for sname , sub in mipm .project_data ['subprojects' ].items ():
478
- structure = False
479
- runtime = False
480
- if sub ['status' ] != 'PRESENT' :
481
- structure = True
482
- if not sub .get ('scan_results' , None ):
483
- runtime = True
484
- else :
485
- rcodes = [r ['scan_results' ]['returncode' ] for r in sub ['scan_results' ] if r .get ('scan_results' , None )]
486
- if sum (rcodes ) > 0 :
487
- runtime = True
488
- if structure or runtime :
489
- failures .append (sub )
490
- with open (filename_failures , "w" ) as f :
491
- json .dump (failures , f , indent = 2 )
557
+ write_failure_report (mipm .project_data , filename_failure_report )
492
558
493
559
if __name__ == "__main__" :
494
560
sys .exit (main ())
0 commit comments