Skip to content

Commit d7c85ff

Browse files
authored
Add tag field to the CodebaseResource #443 (#456)
* Add tag field to the CodebaseResource #443 Craft and store layer_tag during the docker pipeline Signed-off-by: Thomas Druez <tdruez@nexb.com> * Fix failing test #443 Signed-off-by: Thomas Druez <tdruez@nexb.com> * Add CAHNGELOG entry #443 Signed-off-by: Thomas Druez <tdruez@nexb.com> * Regen the expected results test files #443 Signed-off-by: Thomas Druez <tdruez@nexb.com> * Skip RPM test if not on linux #443 Signed-off-by: Thomas Druez <tdruez@nexb.com>
1 parent af93c1c commit d7c85ff

19 files changed

Lines changed: 47270 additions & 40756 deletions

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ v31.0.0 (next)
2727
Reference: https://tracker.debian.org/pkg/wait-for-it
2828
https://github.com/nexB/scancode.io/issues/387
2929

30+
- Add a "tag" field on the CodebaseResource model.
31+
The layer details are stored in this field in the "docker" pipeline.
32+
https://github.com/nexB/scancode.io/issues/443
33+
3034
- Add support for multiple inputs in the LoadInventory pipeline.
3135
https://github.com/nexB/scancode.io/issues/451
3236

scancodeio/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@
224224
"level": env.str("SCANCODEIO_LOG_LEVEL", "INFO"),
225225
"propagate": False,
226226
},
227+
"django": {
228+
"handlers": ["null"] if IS_TESTS else ["console"],
229+
"propagate": False,
230+
},
227231
},
228232
}
229233

scanpipe/filters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ class Meta:
149149
"sha512",
150150
"size",
151151
"status",
152+
"tag",
152153
"type",
153154
"name",
154155
"extension",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.0.5 on 2022-06-20 08:39
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('scanpipe', '0017_alter_discoveredpackage_package_uid_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='codebaseresource',
15+
name='tag',
16+
field=models.CharField(blank=True, max_length=50),
17+
),
18+
]

scanpipe/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,10 @@ class CodebaseResource(
13881388
max_length=30,
13891389
help_text=_("Analysis status for this resource."),
13901390
)
1391+
tag = models.CharField(
1392+
blank=True,
1393+
max_length=50,
1394+
)
13911395

13921396
class Type(models.TextChoices):
13931397
"""

scanpipe/pipes/__init__.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,25 @@
3636
logger = logging.getLogger("scanpipe.pipes")
3737

3838

39-
def make_codebase_resource(project, location, rootfs_path=None):
39+
def make_codebase_resource(project, location, **extra_fields):
4040
"""
4141
Creates a CodebaseResource instance in the database for the given `project`.
4242
4343
The provided `location` is the absolute path of this resource.
4444
It must be rooted in `project.codebase_path` as only the relative path within the
4545
project codebase/ directory is stored in the database.
4646
47-
`rootfs_path` is an optional path relative to a rootfs root within an
48-
Image/VM filesystem context. e.g.: "/var/log/file.log"
47+
Extra fields can be provided as keywords arguments to this function call:
48+
49+
make_codebase_resource(
50+
project=project,
51+
location=resource.location,
52+
rootfs_path=resource.path,
53+
tag=layer_tag,
54+
)
55+
56+
In this example, `rootfs_path` is an optional path relative to a rootfs root
57+
within an Image/VM filesystem context. e.g.: "/var/log/file.log"
4958
5059
All paths use the POSIX separators.
5160
@@ -56,8 +65,8 @@ def make_codebase_resource(project, location, rootfs_path=None):
5665
relative_path = Path(location).relative_to(project.codebase_path)
5766
resource_data = scancode.get_resource_info(location=location)
5867

59-
if rootfs_path:
60-
resource_data["rootfs_path"] = rootfs_path
68+
if extra_fields:
69+
resource_data.update(**extra_fields)
6170

6271
codebase_resource = CodebaseResource(
6372
project=project,

scanpipe/pipes/docker.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def extract_image_from_tarball(input_tarball, extract_target, verify=True):
6060
Path object and collects the extracted images.
6161
6262
Returns the `images` and an `errors` list of error messages that may have
63-
happen during the extraction.
63+
happened during the extraction.
6464
"""
6565
errors = extract_tar(location=input_tarball, target_dir=extract_target)
6666
images = Image.get_images_from_dir(
@@ -126,16 +126,41 @@ def get_image_data(image, layer_path_segments=2):
126126
return image_data
127127

128128

129+
def get_layer_tag(image_id, layer_id, layer_index, id_length=6):
130+
"""
131+
Returns a "tag" crafted from the provided `image_id`, `layer_id`, and `layer_index`.
132+
The purpose of this tag is to be short, clear and sortable.
133+
134+
For instance, given an image with an id:
135+
785df58b6b3e120f59bce6cd10169a0c58b8837b24f382e27593e2eea011a0d8
136+
137+
and two layers from bottom to top as:
138+
0690c89adf3e8c306d4ced085fc16d1d104dcfddd6dc637e141fa78be242a707
139+
7a1d89d2653e8e4aa9011fd95034a4857109d6636f2ad32df470a196e5dd1585
140+
141+
we would get these two tags:
142+
img-785df5-layer-01-0690c8
143+
img-785df5-layer-02-7a1d89
144+
"""
145+
short_image_id = image_id[:id_length]
146+
short_layer_id = layer_id[:id_length]
147+
return f"img-{short_image_id}-layer-{layer_index:02}-{short_layer_id}"
148+
149+
129150
def create_codebase_resources(project, image):
130151
"""
131152
Creates the CodebaseResource for an `image` in a `project`.
132153
"""
133-
for layer_resource in image.get_layers_resources():
134-
pipes.make_codebase_resource(
135-
project=project,
136-
location=layer_resource.location,
137-
rootfs_path=layer_resource.path,
138-
)
154+
for layer_index, layer in enumerate(image.layers, start=1):
155+
layer_tag = get_layer_tag(image.image_id, layer.layer_id, layer_index)
156+
157+
for resource in layer.get_resources():
158+
pipes.make_codebase_resource(
159+
project=project,
160+
location=resource.location,
161+
rootfs_path=resource.path,
162+
tag=layer_tag,
163+
)
139164

140165

141166
def scan_image_for_system_packages(project, image, detect_licenses=True):

scanpipe/templates/scanpipe/resource_list.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<th>Extension</th>
2727
<th>Programming language</th>
2828
<th>Mime type</th>
29+
<th>Tag</th>
2930
<th>License expressions</th>
3031
{% if include_compliance_alert %}
3132
<th>Compliance alert</th>
@@ -58,6 +59,9 @@
5859
<td>
5960
<a href="?mime_type={{ resource.mime_type }}" class="is-black-link">{{ resource.mime_type }}</a>
6061
</td>
62+
<td>
63+
<a href="?tag={{ resource.tag }}" class="is-black-link">{{ resource.tag }}</a>
64+
</td>
6165
<td>
6266
<ul>
6367
{% for license_expression in resource.unique_license_expressions %}

0 commit comments

Comments
 (0)