46
46
import subprocess
47
47
import sys
48
48
from argparse import ArgumentParser
49
+ import argparse
49
50
50
51
#hub = HubInstance()
51
52
@@ -59,15 +60,19 @@ def __init__(self, workdir, scratch = True):
59
60
self .imagedir = self .workdir + "/container"
60
61
self .imagefile = self .workdir + "/image.tar"
61
62
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 ()
69
64
self .docker_path = self .locate_docker ()
70
65
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
+
71
76
def locate_docker (self ):
72
77
os .environ ['PATH' ] += os .pathsep + '/usr/local/bin'
73
78
args = []
@@ -146,7 +151,7 @@ def detect_run(self, options=['--help']):
146
151
147
152
class ContainerImageScanner ():
148
153
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 ):
150
155
self .hub = hub
151
156
self .hub_detect = Detector (hub )
152
157
self .docker = DockerWrapper (workdir )
@@ -158,8 +163,12 @@ def __init__(self, hub, container_image_name, workdir='/tmp/workdir'):
158
163
else :
159
164
self .image_name = container_image_name [:cindex ]
160
165
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
161
169
162
170
def prepare_container_image (self ):
171
+ self .docker .initdir ()
163
172
self .docker .pull_container_image (self .container_image_name )
164
173
self .docker .save_container_image (self .container_image_name )
165
174
self .docker .unravel_container ()
@@ -184,15 +193,51 @@ def process_container_image(self):
184
193
num = num + 1
185
194
print (json .dumps (self .layers , indent = 4 ))
186
195
187
- def generate_project_structures (self ):
196
+ def generate_project_structures (self , base_layers = None ):
188
197
main_project_release = self .hub .get_or_create_project_version (self .image_name , self .image_version )
189
198
190
199
for layer in self .layers :
191
200
parameters = {}
192
201
parameters ['description' ] = layer ['command' ]['created_by' ]
193
202
sub_project_release = self .hub .get_or_create_project_version (layer ['name' ], self .image_version , parameters = parameters )
194
203
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
+
196
241
def generate_single_layer_project_structure (self , layer_number ):
197
242
main_project_release = self .hub .get_or_create_project_version (self .image_name , self .image_version )
198
243
@@ -224,7 +269,9 @@ def submit_single_layer_scan(self, layer_number):
224
269
225
270
def cleanup_project_structure (self ):
226
271
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
+
228
275
components = self .hub .get_version_components (release )
229
276
230
277
print (components )
@@ -234,9 +281,44 @@ def cleanup_project_structure(self):
234
281
sub_version_name = item ['componentVersionName' ]
235
282
sub_release = self .hub .get_or_create_project_version (sub_name , sub_version_name )
236
283
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 ))
237
286
print (self .hub .delete_project_by_name (sub_name ))
238
287
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
+
240
322
241
323
def scan_container_image (imagespec , layer_number = 0 ):
242
324
@@ -251,7 +333,16 @@ def scan_container_image(imagespec, layer_number=0):
251
333
scanner .generate_single_layer_project_structure (layer_number )
252
334
scanner .submit_single_layer_scan (int (layer_number ))
253
335
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
+
255
346
def clean_container_project (imagespec ):
256
347
hub = HubInstance ()
257
348
scanner = ContainerImageScanner (hub , imagespec )
@@ -269,14 +360,31 @@ def main(argv=None):
269
360
parser .add_argument ('imagespec' , help = "Container image tag, e.g. repository/imagename:version" )
270
361
parser .add_argument ('--cleanup' , default = False , help = "Delete project hierarchy only. Do not scan" )
271
362
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)" )
272
366
args = parser .parse_args ()
273
367
274
368
print (args );
275
-
276
- hub = HubInstance ()
277
369
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
+
278
382
if args .cleanup :
279
383
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 )
280
388
else :
281
389
if args .rescan_layer == 0 :
282
390
clean_container_project (args .imagespec )
0 commit comments