Skip to content

Commit 0086fee

Browse files
authored
Use the common extraction API with extractcode for the Docker pipeline #70 (#108)
* Use the same extraction API from scancode for rootfs and docker pipelines #70 Signed-off-by: Thomas Druez <tdruez@nexb.com> * Move the extract_images and extract_layers logic into the docker pipes module #70 Signed-off-by: Thomas Druez <tdruez@nexb.com> * Add the tag_empty_files pipe in the docker pipeline #70 For consistency with the root_filesystems pipeline Signed-off-by: Thomas Druez <tdruez@nexb.com>
1 parent 2ea50a6 commit 0086fee

6 files changed

Lines changed: 71 additions & 54 deletions

File tree

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
### v1.1.1 (unreleased)
55

6+
- Use the extractcode API for the Docker pipeline.
7+
This change helps with performance and results consistency between pipelines.
8+
https://github.com/nexB/scancode.io/issues/70
9+
610
- Implement cache to prevent scanning multiple times a duplicated codebase resource.
711
https://github.com/nexB/scancode.io/issues/70
812

scanpipe/pipelines/docker.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
# ScanCode.io is a free software code scanning tool from nexB Inc. and others.
2121
# Visit https://github.com/nexB/scancode.io for support and download.
2222

23-
import os
24-
2523
from scanpipe import pipes
2624
from scanpipe.pipelines import Pipeline
2725
from scanpipe.pipes import docker
@@ -38,16 +36,17 @@ def extract_images(self):
3836
"""
3937
Extract the images from tarballs.
4038
"""
41-
self.images = docker.get_and_extract_images_from_image_tarballs(self.project)
39+
self.images, errors = docker.extract_images_from_inputs(self.project)
40+
if errors:
41+
self.add_error("\n".join(errors))
4242

4343
def extract_layers(self):
4444
"""
4545
Extract layers from images.
4646
"""
47-
for image in self.images:
48-
image_dirname = os.path.basename(image.base_location)
49-
target_dir = str(self.project.codebase_path / image_dirname)
50-
image.extract_layers(target_dir=target_dir)
47+
errors = docker.extract_layers_from_images(self.project, self.images)
48+
if errors:
49+
self.add_error("\n".join(errors))
5150

5251
def find_images_linux_distro(self):
5352
"""
@@ -86,6 +85,12 @@ def tag_uninteresting_codebase_resources(self):
8685
docker.tag_whiteout_codebase_resources(self.project)
8786
rootfs.tag_uninteresting_codebase_resources(self.project)
8887

88+
def tag_empty_files(self):
89+
"""
90+
Flag empty files.
91+
"""
92+
rootfs.tag_empty_codebase_resources(self.project)
93+
8994
def scan_for_application_packages(self):
9095
"""
9196
Scan unknown resources for packages infos.
@@ -118,6 +123,7 @@ def tag_not_analyzed_codebase_resources(self):
118123
collect_and_create_codebase_resources,
119124
collect_and_create_system_packages,
120125
tag_uninteresting_codebase_resources,
126+
tag_empty_files,
121127
scan_for_application_packages,
122128
scan_for_files,
123129
analyze_scanned_files,

scanpipe/pipelines/root_filesystems.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222

2323
import os
2424

25-
from extractcode.extract import extract_file
26-
2725
from scanpipe import pipes
2826
from scanpipe.pipelines import Pipeline
2927
from scanpipe.pipes import rootfs
@@ -40,13 +38,11 @@ def extract_input_files_to_codebase_directory(self):
4038
Extract root filesystem input archives with extractcode.
4139
"""
4240
input_files = self.project.inputs("*")
43-
target = str(self.project.codebase_path)
41+
target_path = self.project.codebase_path
4442
extract_errors = []
4543

4644
for input_file in input_files:
47-
for event in extract_file(input_file, target):
48-
if event.done:
49-
extract_errors.extend(event.errors)
45+
extract_errors = scancode.extract(input_file, target_path)
5046

5147
if extract_errors:
5248
self.add_error("\n".join(extract_errors))
@@ -91,17 +87,17 @@ def tag_uninteresting_codebase_resources(self):
9187
"""
9288
rootfs.tag_uninteresting_codebase_resources(self.project)
9389

94-
def scan_for_application_packages(self):
90+
def tag_empty_files(self):
9591
"""
96-
Scan unknown resources for packages infos.
92+
Flag empty files.
9793
"""
98-
scancode.scan_for_application_packages(self.project)
94+
rootfs.tag_empty_codebase_resources(self.project)
9995

100-
def ignore_empty_files(self):
96+
def scan_for_application_packages(self):
10197
"""
102-
Skip and mark as ignored any empty file.
98+
Scan unknown resources for packages infos.
10399
"""
104-
rootfs.tag_empty_codebase_resources(self.project)
100+
scancode.scan_for_application_packages(self.project)
105101

106102
def match_not_analyzed_to_system_packages(self):
107103
"""
@@ -149,8 +145,8 @@ def tag_not_analyzed_codebase_resources(self):
149145
collect_and_create_codebase_resources,
150146
collect_and_create_system_packages,
151147
tag_uninteresting_codebase_resources,
148+
tag_empty_files,
152149
scan_for_application_packages,
153-
ignore_empty_files,
154150
match_not_analyzed_to_system_packages,
155151
scan_for_files,
156152
analyze_scanned_files,

scanpipe/pipes/docker.py

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,48 +31,47 @@
3131
from scanpipe import pipes
3232
from scanpipe.models import CodebaseResource
3333
from scanpipe.pipes import rootfs
34+
from scanpipe.pipes import scancode
3435

3536
logger = logging.getLogger(__name__)
3637

3738

38-
def get_images_from_extracted_codebase(project):
39+
def extract_images_from_inputs(project):
3940
"""
40-
Yield images collected from the project "codebase" directory. The
41-
image tarball should be extracted there first, with its manifest.json at the
42-
root.
41+
Collect all the tarballs from the `project` input/ work directory, extract each
42+
tarball to the tmp/ work directory and collect the images.
43+
Return the `images` and `errors` that may have happen during the extraction.
4344
"""
44-
codebase = project.codebase_path.absolute()
45-
for image in Image.get_images_from_dir(location=codebase):
46-
yield image
45+
target_path = project.tmp_path
46+
images = []
47+
errors = []
4748

49+
for input_tarball in project.inputs(pattern="*.tar*"):
50+
extract_target = target_path / f"{input_tarball.name}-extract"
51+
extract_errors = scancode.extract(input_tarball, extract_target)
52+
images.extend(Image.get_images_from_dir(extract_target))
53+
errors.extend(extract_errors)
4854

49-
def get_and_extract_images_from_image_tarballs(project, force_extract=False):
50-
"""
51-
Yield images collected from the project "codebase" directory.
52-
The process is to:
53-
1. collect all the tarballs from this directory.
54-
2. for each, extract that under a -extract directory and collect its images.
55+
return images, errors
5556

56-
If `force_extract` is False, do not extract if already extracted.
57-
"""
58-
all_images = []
5957

60-
for tarball in project.inputs(pattern="*.tar*"):
61-
tarball_location = str(tarball.absolute())
62-
if tarball_location.endswith("-extract"):
63-
continue
64-
extracted_location = tarball_location + "-extract"
65-
tarball_images = Image.get_images_from_tarball(
66-
location=tarball_location,
67-
target_dir=extracted_location,
68-
force_extract=force_extract,
69-
)
58+
def extract_layers_from_images(project, images):
59+
"""
60+
Extract all the layers from provided `images` into the `project` codebase/ work
61+
directory.
62+
Return the `errors` that may have happen during the extraction.
63+
"""
64+
errors = []
7065

71-
for image in tarball_images:
72-
logger.info("Collected Docker image: {}".format(image.image_id))
73-
all_images.append(image)
66+
for image in images:
67+
image_dirname = Path(image.base_location).name
68+
target_path = project.codebase_path / image_dirname
7469

75-
return all_images
70+
for layer in image.layers:
71+
extract_target = target_path / layer.layer_id
72+
extract_errors = scancode.extract(layer.layer_location, extract_target)
73+
errors.extend(extract_errors)
74+
layer.extracted_to_location = str(extract_target)
7675

7776

7877
def get_image_data(image):

scanpipe/pipes/rootfs.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def match_not_analyzed(
298298

299299
def tag_empty_codebase_resources(project):
300300
"""
301-
Tag remaining empty files as ignored
301+
Tag empty files as ignored.
302302
"""
303303
project.codebaseresources.select_for_update().filter(
304304
type__exact="file",
@@ -324,9 +324,7 @@ def tag_uninteresting_codebase_resources(project):
324324
"/proc/",
325325
"/dev/",
326326
"/run/",
327-
) + (
328-
# alpine specific
329-
"/lib/apk/db/",
327+
"/lib/apk/db/", # alpine specific
330328
)
331329

332330
qs = project.codebaseresources.no_status()

scanpipe/pipes/scancode.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import packagedcode
3131
from commoncode import fileutils
3232
from commoncode.resource import VirtualCodebase
33+
from extractcode.extract import extract_file
3334
from packageurl import PackageURL
3435
from scancode import ScancodeError
3536
from scancode import api as scancode_api
@@ -45,6 +46,19 @@
4546
"""
4647

4748

49+
def extract(location, target):
50+
"""
51+
Wraps the `extractcode.extract_file` to execute the extraction and return errors.
52+
"""
53+
errors = []
54+
55+
for event in extract_file(location, target):
56+
if event.done:
57+
errors.extend(event.errors)
58+
59+
return errors
60+
61+
4862
def get_resource_info(location):
4963
"""
5064
Return a mapping suitable for the creation of a new CodebaseResource.

0 commit comments

Comments
 (0)