Skip to content

Commit dbdd960

Browse files
author
Glenn Snyder
committed
2 parents 01f46fe + 566c5a1 commit dbdd960

File tree

1 file changed

+123
-15
lines changed

1 file changed

+123
-15
lines changed

examples/scan_docker_image.py

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import subprocess
4747
import sys
4848
from argparse import ArgumentParser
49+
import argparse
4950

5051
#hub = HubInstance()
5152

@@ -59,15 +60,19 @@ def __init__(self, workdir, scratch = True):
5960
self.imagedir = self.workdir + "/container"
6061
self.imagefile = self.workdir + "/image.tar"
6162
if scratch:
62-
if os.path.exists(self.workdir):
63-
if os.path.isdir(self.workdir):
64-
shutil.rmtree(self.workdir)
65-
else:
66-
os.remove(self.workdir)
67-
os.makedirs(self.workdir, 0o755, True)
68-
os.makedirs(self.workdir + "/container", 0o755, True)
63+
self.initdir()
6964
self.docker_path = self.locate_docker()
7065

66+
def initdir(self):
67+
if os.path.exists(self.workdir):
68+
if os.path.isdir(self.workdir):
69+
shutil.rmtree(self.workdir)
70+
else:
71+
os.remove(self.workdir)
72+
os.makedirs(self.workdir, 0o755, True)
73+
os.makedirs(self.workdir + "/container", 0o755, True)
74+
75+
7176
def locate_docker(self):
7277
os.environ['PATH'] += os.pathsep + '/usr/local/bin'
7378
args = []
@@ -146,7 +151,7 @@ def detect_run(self, options=['--help']):
146151

147152
class ContainerImageScanner():
148153

149-
def __init__(self, hub, container_image_name, workdir='/tmp/workdir'):
154+
def __init__(self, hub, container_image_name, workdir='/tmp/workdir', dockerfile=None, base_image=None, omit_base_layers=False):
150155
self.hub = hub
151156
self.hub_detect = Detector(hub)
152157
self.docker = DockerWrapper(workdir)
@@ -158,8 +163,12 @@ def __init__(self, hub, container_image_name, workdir='/tmp/workdir'):
158163
else:
159164
self.image_name = container_image_name[:cindex]
160165
self.image_version = container_image_name[cindex+1:]
166+
self.dockerfile = dockerfile
167+
self.base_image = base_image
168+
self.omit_base_layers = omit_base_layers
161169

162170
def prepare_container_image(self):
171+
self.docker.initdir()
163172
self.docker.pull_container_image(self.container_image_name)
164173
self.docker.save_container_image(self.container_image_name)
165174
self.docker.unravel_container()
@@ -184,15 +193,51 @@ def process_container_image(self):
184193
num = num + 1
185194
print (json.dumps(self.layers, indent=4))
186195

187-
def generate_project_structures(self):
196+
def generate_project_structures(self, base_layers=None):
188197
main_project_release = self.hub.get_or_create_project_version(self.image_name, self.image_version)
189198

190199
for layer in self.layers:
191200
parameters = {}
192201
parameters['description'] = layer['command']['created_by']
193202
sub_project_release = self.hub.get_or_create_project_version(layer['name'], self.image_version, parameters=parameters)
194203
self.hub.add_version_as_component(main_project_release, sub_project_release)
195-
204+
# Process base and add-on parts
205+
if base_layers:
206+
base_image_version = self.image_version + "__base_layers"
207+
addon_image_version = self.image_version + "_addon_layers"
208+
base = []
209+
addon = []
210+
211+
for layer in self.layers:
212+
if layer['path'] in base_layers:
213+
base.append(layer)
214+
else:
215+
addon.append(layer)
216+
217+
print ("Number of base layers {}".format(len(base)))
218+
print ("Number of addon layers {}".format(len(addon)))
219+
220+
if (len(base) > 0):
221+
main_project_release_addon = self.hub.get_or_create_project_version(self.image_name, addon_image_version)
222+
if not self.omit_base_layers:
223+
main_project_release_base = self.hub.get_or_create_project_version(self.image_name, base_image_version)
224+
for layer in base:
225+
parameters = {}
226+
parameters['description'] = layer['command']['created_by']
227+
sub_project_release = self.hub.get_or_create_project_version(layer['name'], self.image_version, parameters=parameters)
228+
self.hub.add_version_as_component(main_project_release_base, sub_project_release)
229+
for layer in addon:
230+
parameters = {}
231+
parameters['description'] = layer['command']['created_by']
232+
sub_project_release = self.hub.get_or_create_project_version(layer['name'], self.image_version, parameters=parameters)
233+
self.hub.add_version_as_component(main_project_release_addon, sub_project_release)
234+
else:
235+
print("************************************************************")
236+
print("* *")
237+
print("* No layers from Dockerfile/base are present in the image *")
238+
print("* *")
239+
print("************************************************************")
240+
196241
def generate_single_layer_project_structure(self, layer_number):
197242
main_project_release = self.hub.get_or_create_project_version(self.image_name, self.image_version)
198243

@@ -224,7 +269,9 @@ def submit_single_layer_scan(self, layer_number):
224269

225270
def cleanup_project_structure(self):
226271
release = self.hub.get_or_create_project_version(self.image_name,self.image_version)
227-
272+
base_release = self.hub.get_or_create_project_version(self.image_name,self.image_version + "__base_layers")
273+
addon_release = self.hub.get_or_create_project_version(self.image_name,self.image_version + "_addon_layers")
274+
228275
components = self.hub.get_version_components(release)
229276

230277
print (components)
@@ -234,9 +281,44 @@ def cleanup_project_structure(self):
234281
sub_version_name = item['componentVersionName']
235282
sub_release = self.hub.get_or_create_project_version(sub_name, sub_version_name)
236283
print(self.hub.remove_version_as_component(release, sub_release))
284+
print(self.hub.remove_version_as_component(base_release, sub_release))
285+
print(self.hub.remove_version_as_component(addon_release, sub_release))
237286
print(self.hub.delete_project_by_name(sub_name))
238287
print(self.hub.delete_project_by_name(self.image_name))
239-
288+
289+
def get_base_layers(self):
290+
if (not self.dockerfile)and (not self.base_image):
291+
raise Exception ("No dockerfile or base image specified")
292+
imagelist = []
293+
294+
if self.dockerfile:
295+
from pathlib import Path
296+
dfile = Path(self.dockerfile)
297+
if not dfile.exists():
298+
raise Exception ("Dockerfile {} does not exist",format(self.dockerfile))
299+
if not dfile.is_file():
300+
raise Exception ("{} is not a file".format(self.dockerfile))
301+
with open(dfile) as f:
302+
for line in f:
303+
if 'FROM' in line.upper():
304+
a = line.split()
305+
if a[0].upper() == 'FROM':
306+
imagelist.append(a[1])
307+
if self.base_image:
308+
imagelist.append(self.base_image)
309+
310+
print (imagelist)
311+
base_layers = []
312+
for image in imagelist:
313+
self.docker.initdir()
314+
self.docker.pull_container_image(image)
315+
self.docker.save_container_image(image)
316+
self.docker.unravel_container()
317+
manifest = self.docker.read_manifest()
318+
print(manifest)
319+
base_layers.extend(manifest[0]['Layers'])
320+
return base_layers
321+
240322

241323
def scan_container_image(imagespec, layer_number=0):
242324

@@ -251,7 +333,16 @@ def scan_container_image(imagespec, layer_number=0):
251333
scanner.generate_single_layer_project_structure(layer_number)
252334
scanner.submit_single_layer_scan(int(layer_number))
253335

254-
336+
def scan_container_image_with_dockerfile(imagespec, dockerfile, base_image, omit_base_layers):
337+
hub = HubInstance()
338+
scanner = ContainerImageScanner(hub, imagespec, dockerfile=dockerfile, base_image=base_image, omit_base_layers=omit_base_layers)
339+
base_layers = scanner.get_base_layers()
340+
print (base_layers)
341+
scanner.prepare_container_image()
342+
scanner.process_container_image()
343+
scanner.generate_project_structures(base_layers)
344+
scanner.submit_layer_scans()
345+
255346
def clean_container_project(imagespec):
256347
hub = HubInstance()
257348
scanner = ContainerImageScanner(hub, imagespec)
@@ -269,14 +360,31 @@ def main(argv=None):
269360
parser.add_argument('imagespec', help="Container image tag, e.g. repository/imagename:version")
270361
parser.add_argument('--cleanup', default=False, help="Delete project hierarchy only. Do not scan")
271362
parser.add_argument('--rescan-layer',default=0, type=int, help="Rescan specific layer in case of failure, 0 - scan as usual")
363+
parser.add_argument('--dockerfile',default=None, type=str, help="Specify dockerfile used to build this container(experimantal), can't use with --base-image")
364+
parser.add_argument('--base-image',default=None, type=str, help="Specify base image used to build this container(experimantal), can't use with --dockerfile")
365+
parser.add_argument('--omit-base-layers',default=False, type=bool, help="Omit base layer (requires --dockerfile or --base-image)")
272366
args = parser.parse_args()
273367

274368
print (args);
275-
276-
hub = HubInstance()
277369

370+
if not args.imagespec:
371+
parser.print_help(sys.stdout)
372+
sys.exit(1)
373+
374+
if args.dockerfile and args.base_image:
375+
parser.print_help(sys.stdout)
376+
sys.exit(1)
377+
378+
if args.omit_base_layers and not (args.dockerfile or args.base_image):
379+
parser.print_help(sys.stdout)
380+
sys.exit(1)
381+
278382
if args.cleanup:
279383
clean_container_project(args.imagespec)
384+
sys.exit(1)
385+
if args.dockerfile or args.base_image:
386+
clean_container_project(args.imagespec)
387+
scan_container_image_with_dockerfile(args.imagespec, args.dockerfile, args.base_image, args.omit_base_layers)
280388
else:
281389
if args.rescan_layer == 0:
282390
clean_container_project(args.imagespec)

0 commit comments

Comments
 (0)