Skip to content

Commit 24d5c9b

Browse files
committed
reporcessing option added
1 parent 35f9b4b commit 24d5c9b

File tree

2 files changed

+92
-26
lines changed

2 files changed

+92
-26
lines changed

examples/client/multi-image/manage_project_structure.py

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
import sys
6262
import arrow
6363

64+
from io import StringIO
65+
6466
from blackduck import Client
6567
from pprint import pprint,pformat
6668

@@ -75,12 +77,48 @@ def __init__(self, args):
7577
with open(args.token_file, 'r') as tf:
7678
self.access_token = tf.readline().strip()
7779
self.no_verify = args.no_verify
80+
self.reprocess_run_file = args.reprocess_run_file
7881
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)
8086

8187
def connect(self):
8288
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+
84122
def init_project_data(self,args):
85123
self.project_data = dict()
86124
self.project_data['project_name'] = args.project_name
@@ -435,15 +473,51 @@ def proceed(self):
435473
self.validate_project_structure()
436474
self.scan_container_images()
437475

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"\nStatus for {subproject['project_name']} {subproject['version_name']}", file = s)
492+
print (f"\tStructural 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\tScan 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())
438512

439513
def parse_command_args():
440514

441515
parser = argparse.ArgumentParser(description=program_description, formatter_class=argparse.RawTextHelpFormatter)
442516
parser.add_argument("-u", "--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
443517
parser.add_argument("-t", "--token-file", required=True, help="File containing access token")
444518
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")
447521
group = parser.add_mutually_exclusive_group()
448522
group.add_argument("-sp", "--subproject-list", required=False, help="List of subprojects to generate with subproject:container:tag")
449523
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():
456530
parser.add_argument("--strict", action='store_true', help="Fail if existing (sub)project versions already exist")
457531
parser.add_argument("--binary", action='store_true', help="Use binary scan for analysis")
458532
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
460540

461541
def main():
462542
from datetime import datetime
@@ -467,28 +547,14 @@ def main():
467547
mipm.proceed()
468548

469549
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"
472553
# write full processing log
473554
with open (filename_complete, "w") as f:
474555
json.dump(mipm.project_data, f, indent=2)
475556

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)
492558

493559
if __name__ == "__main__":
494560
sys.exit(main())

examples/client/multi-image/scan_docker_image_lite.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ def prepare_container_image(self):
268268
for item in history:
269269
if not item.get('empty_layer', None):
270270
layer_count += 1
271-
match = re.search('echo (.+?)_group_end', item['created_by'])
271+
match = re.search('echo (.+?)_group_end', item.get('created_by',''))
272272
if match:
273273
found = match.group(1)
274274
if len(history_grouping):
@@ -350,8 +350,8 @@ def process_oci_container_image_by_user_defined_groups(self):
350350
self.config = self.docker.read_config()
351351

352352
self.layers = self.config['history']
353-
tagged_layers = [x for x in self.layers if '_group_end' in x.get('created_by')]
354-
groups = {re.search('echo (.+?)_group_end', str(x['created_by'])).group(1): self.layers.index(x) for x in tagged_layers}
353+
tagged_layers = [x for x in self.layers if '_group_end' in x.get('created_by','')]
354+
groups = {re.search('echo (.+?)_group_end', str(x.get('created_by',''))).group(1): self.layers.index(x) for x in tagged_layers}
355355
logging.debug(f"Container configuration defines following groups {groups}")
356356
layer_paths = self.manifest[0]['Layers'].copy()
357357
empty_layers = [x for x in self.layers if x.get('empty_layer', False)]

0 commit comments

Comments
 (0)