Skip to content

Commit 3253df3

Browse files
author
Malte Münch
authored
Merge pull request #52 from gardenlinux/48-do-we-have-to-include-this-applicationvndgardenlinuximagechecksumsha256
Removal of additional sha256 digest in manifest elements
2 parents b5f1f0e + 48afd14 commit 3253df3

File tree

5 files changed

+199
-70
lines changed

5 files changed

+199
-70
lines changed

src/python_gardenlinux_lib/features/parse_features.py

Lines changed: 145 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@
77
import subprocess
88
from typing import Optional
99

10+
# It is important that this list is sorted in descending length of the entries
11+
GL_MEDIA_TYPES = [
12+
"gcpimage.tar.gz.log",
13+
"firecracker.tar.gz",
14+
"gcpimage.tar.gz",
15+
"pxe.tar.gz.log",
16+
"manifest.log",
17+
"release.log",
18+
"pxe.tar.gz",
19+
"qcow2.log",
20+
"test-log",
21+
"manifest",
22+
"vmdk.log",
23+
"tar.log",
24+
"release",
25+
"vhd.log",
26+
"ova.log",
27+
"raw.log",
28+
"tar.gz",
29+
"qcow2",
30+
"tar",
31+
"iso",
32+
"oci",
33+
"vhd",
34+
"vmdk",
35+
"ova",
36+
"raw",
37+
]
38+
39+
1040
GL_MEDIA_TYPE_LOOKUP = {
1141
"tar": "application/io.gardenlinux.image.archive.format.tar",
1242
"tar.gz": "application/io.gardenlinux.image.archive.format.tar.gz",
@@ -20,6 +50,19 @@
2050
"vmdk": "application/io.gardenlinux.image.format.vmdk",
2151
"ova": "application/io.gardenlinux.image.format.ova",
2252
"raw": "application/io.gardenlinux.image.archive.format.raw",
53+
"manifest.log": "application/io.gardenlinux.log",
54+
"release.log": "application/io.gardenlinux.log",
55+
"test-log": "application/io.gardenlinux.test-log",
56+
"manifest": "application/io.gardenlinux.manifest",
57+
"tar.log": "application/io.gardenlinux.log",
58+
"release": "application/io.gardenlinux.release",
59+
"raw.log": "application/io.gardenlinux.log",
60+
"qcow2.log": "application/io.gardenlinux.log",
61+
"pxe.tar.gz.log": "application/io.gardenlinux.log",
62+
"gcpimage.tar.gz.log": "application/io.gardenlinux.log",
63+
"vmdk.log": "application/io.gardenlinux.log",
64+
"vhd.log": "application/io.gardenlinux.log",
65+
"ova.log": "application/io.gardenlinux.log",
2366
}
2467

2568

@@ -88,53 +131,82 @@ def construct_layer_metadata(
88131
return {
89132
"file_name": f"{cname}-{arch}-{version}-{commit}.{filetype}",
90133
"media_type": media_type,
134+
"annotations": {"io.gardenlinux.image.layer.architecture": arch},
135+
}
136+
137+
138+
def construct_layer_metadata_from_filename(filename: str, arch: str) -> dict:
139+
"""
140+
:param str filename: filename of the blob
141+
:param str arch: the arch of the target image
142+
:return: dict of oci layer metadata for a given layer file
143+
"""
144+
media_type = lookup_media_type_for_file(filename)
145+
return {
146+
"file_name": filename,
147+
"media_type": media_type,
148+
"annotations": {"io.gardenlinux.image.layer.architecture": arch},
91149
}
92150

93151

94-
def get_oci_metadata(cname: str, version: str, gardenlinux_root: str):
152+
def get_file_set_from_cname(cname: str, version: str, arch: str, gardenlinux_root: str):
95153
"""
96154
:param str cname: the target cname of the image
97-
:param str version: the target version of the image
155+
:param str version: the version of the target image
156+
:param str arch: the arch of the target image
98157
:param str gardenlinux_root: path of garden linux src root
99-
:return: list of dicts, where each dict represents a layer
158+
:return: set of file names for a given cname
100159
"""
101-
oci_layer_metadata_list = list()
160+
file_set = set()
102161
features_by_type = get_features_dict(cname, gardenlinux_root)
103162
commit_str = get_gardenlinux_commit(gardenlinux_root, 8)
104163

105164
if commit_str == "local":
106165
raise ValueError("Using local commit. Refusing to upload to OCI Registry")
107-
108-
for arch in ["amd64", "arm64"]:
109-
for platform in features_by_type["platform"]:
110-
image_file_types = deduce_image_filetype(
111-
f"{gardenlinux_root}/features/{platform}"
112-
)
113-
archive_file_types = deduce_archive_filetype(
114-
f"{gardenlinux_root}/features/{platform}"
166+
for platform in features_by_type["platform"]:
167+
image_file_types = deduce_filetypes(f"{gardenlinux_root}/features/{platform}")
168+
for ft in image_file_types:
169+
file_set.add(
170+
f"{cname}-{arch}-{version}-{commit_str}.{ft}",
115171
)
116-
# Allow multiple image scripts per feature
117-
if not image_file_types:
118-
image_file_types.append("raw")
119-
if not archive_file_types:
120-
image_file_types.append("tar")
121-
for ft in archive_file_types:
122-
cur_layer_metadata = construct_layer_metadata(
123-
ft, cname, version, arch, commit_str
124-
)
125-
cur_layer_metadata["annotations"] = {
126-
"io.gardenlinux.image.layer.architecture": arch
127-
}
128-
oci_layer_metadata_list.append(cur_layer_metadata)
129-
# Allow multiple convert scripts per feature
130-
for ft in image_file_types:
131-
cur_layer_metadata = construct_layer_metadata(
132-
ft, cname, version, arch, commit_str
133-
)
134-
cur_layer_metadata["annotations"] = {
135-
"io.gardenlinux.image.layer.architecture": arch
136-
}
137-
oci_layer_metadata_list.append(cur_layer_metadata)
172+
return file_set
173+
174+
175+
def get_oci_metadata_from_fileset(fileset: set, arch: str):
176+
"""
177+
:param str arch: arch of the target image
178+
:param set fileset: a list of filenames (not paths) to set oci_metadata for
179+
:return: list of dicts, where each dict represents a layer
180+
"""
181+
oci_layer_metadata_list = list()
182+
183+
for file in fileset:
184+
oci_layer_metadata_list.append(
185+
construct_layer_metadata_from_filename(file, arch)
186+
)
187+
188+
return oci_layer_metadata_list
189+
190+
191+
def get_oci_metadata(cname: str, version: str, arch: str, gardenlinux_root: str):
192+
"""
193+
:param str cname: the target cname of the image
194+
:param str version: the target version of the image
195+
:param str arch: arch of the target image
196+
:param str gardenlinux_root: path of garden linux src root
197+
:return: list of dicts, where each dict represents a layer
198+
"""
199+
200+
# This is the feature deduction approach (glcli oci push)
201+
file_set = get_file_set_from_cname(cname, version, arch, gardenlinux_root)
202+
203+
# This is the tarball extraction approach (glcli oci push-tarball)
204+
oci_layer_metadata_list = list()
205+
206+
for file in file_set:
207+
oci_layer_metadata_list.append(
208+
construct_layer_metadata_from_filename(file, arch)
209+
)
138210

139211
return oci_layer_metadata_list
140212

@@ -148,7 +220,21 @@ def lookup_media_type_for_filetype(filetype: str) -> str:
148220
return GL_MEDIA_TYPE_LOOKUP[filetype]
149221
else:
150222
raise ValueError(
151-
f"No media type for {filetype} is defined. You may want to add the definition to parse_features_lib"
223+
f"media type for {filetype} is not defined. You may want to add the definition to parse_features_lib"
224+
)
225+
226+
227+
def lookup_media_type_for_file(filename: str) -> str:
228+
"""
229+
:param str filename: filename of the target layer
230+
:return: mediatype
231+
"""
232+
for suffix in GL_MEDIA_TYPES:
233+
if filename.endswith(suffix):
234+
return GL_MEDIA_TYPE_LOOKUP[suffix]
235+
else:
236+
raise ValueError(
237+
f"media type for {filename} is not defined. You may want to add the definition to parse_features_lib"
152238
)
153239

154240

@@ -163,25 +249,40 @@ def deduce_feature_name(feature_dir: str):
163249
return parsed["name"]
164250

165251

166-
def deduce_archive_filetype(feature_dir):
252+
def deduce_archive_filetypes(feature_dir):
253+
"""
254+
:param str feature_dir: Directory of single Feature
255+
:return: str list of filetype for archive
256+
"""
257+
return deduce_filetypes_from_string(feature_dir, "image")
258+
259+
260+
def deduce_image_filetypes(feature_dir):
167261
"""
168262
:param str feature_dir: Directory of single Feature
169-
:return: str of filetype for archive
263+
:return: str list of filetype for image
170264
"""
171-
return deduce_filetype_from_string(feature_dir, "image")
265+
return deduce_filetypes_from_string(feature_dir, "convert")
172266

173267

174-
def deduce_image_filetype(feature_dir):
268+
def deduce_filetypes(feature_dir):
175269
"""
176270
:param str feature_dir: Directory of single Feature
177-
:return: str of filetype for image
271+
:return: str list of filetypes for the feature
178272
"""
179-
return deduce_filetype_from_string(feature_dir, "convert")
273+
image_file_types = deduce_image_filetypes(feature_dir)
274+
archive_file_types = deduce_archive_filetypes(feature_dir)
275+
if not image_file_types:
276+
image_file_types.append("raw")
277+
if not archive_file_types:
278+
archive_file_types.append("tar")
279+
image_file_types.extend(archive_file_types)
280+
return image_file_types
180281

181282

182-
def deduce_filetype_from_string(feature_dir: str, script_base_name: str):
283+
def deduce_filetypes_from_string(feature_dir: str, script_base_name: str):
183284
"""
184-
Garden Linux features can optionally have a image.<filetype> or convert.<filetype> script,
285+
Garden Linux features can optionally have an image.<filetype> or convert.<filetype> script,
185286
where the <filetype> indicates the target filetype.
186287
187288
image.<filetype> script converts the .tar to <filetype> archive.
@@ -229,7 +330,7 @@ def read_feature_files(feature_dir):
229330
)
230331
feature_graph.add_edge(node, ref, attr=attr)
231332
if not networkx.is_directed_acyclic_graph(feature_graph):
232-
raise ValueError("Graph is not directed asyclic graph")
333+
raise ValueError("Graph is not directed acyclic graph")
233334
return feature_graph
234335

235336

src/python_gardenlinux_lib/oras/registry.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ def NewPlatform(architecture: str, version: str) -> dict:
7474

7575

7676
def NewManifestMetadata(
77-
digest: str, size: int, annotaions: dict, platform_data: dict
77+
digest: str, size: int, annotations: dict, platform_data: dict
7878
) -> dict:
7979
manifest_meta_data = copy.deepcopy(EmptyManifestMetadata)
8080
manifest_meta_data["mediaType"] = "application/vnd.oci.image.manifest.v1+json"
8181
manifest_meta_data["digest"] = digest
8282
manifest_meta_data["size"] = size
83-
manifest_meta_data["annotations"] = annotaions
83+
manifest_meta_data["annotations"] = annotations
8484
manifest_meta_data["platform"] = platform_data
8585
manifest_meta_data["artifactType"] = ""
8686
return manifest_meta_data
@@ -114,7 +114,10 @@ def create_config_from_dict(conf: dict, annotations: dict) -> Tuple[dict, str]:
114114
def construct_manifest_entry_signed_data_string(
115115
cname: str, version: str, new_manifest_metadata: dict, architecture: str
116116
) -> str:
117-
data_to_sign = f"versio:{version} cname{cname} architecture:{architecture} manifest-size:{new_manifest_metadata['size']} manifest-digest:{new_manifest_metadata['digest']}"
117+
data_to_sign = (
118+
f"version:{version} cname:{cname} architecture:{architecture} manifest-size"
119+
f":{new_manifest_metadata['size']} manifest-digest:{new_manifest_metadata['digest']}"
120+
)
118121
return data_to_sign
119122

120123

@@ -494,9 +497,7 @@ def verify_manifest_signature(self, manifest: dict):
494497
if annotation_signed_string_key not in layer["annotations"]:
495498
raise ValueError(f"layer is not signed. layer: {layer}")
496499
media_type = layer["mediaType"]
497-
checksum_sha256 = layer["annotations"][
498-
"application/vnd.gardenlinux.image.checksum.sha256"
499-
]
500+
checksum_sha256 = layer["digest"].removeprefix("sha256:")
500501
signature = layer["annotations"][annotation_signature_key]
501502
signed_data = layer["annotations"][annotation_signed_string_key]
502503
signed_data_expected = construct_layer_signed_data_string(
@@ -559,20 +560,21 @@ def push_image_manifest(
559560
architecture: str,
560561
cname: str,
561562
version: str,
562-
gardenlinux_root: str,
563563
build_artifacts_dir: str,
564+
oci_metadata: list,
564565
):
565566
"""
566567
creates and pushes an image manifest
567568
569+
:param oci_metadata: a list of filenames and their OCI metadata, can be constructed with get_oci_metadata
568570
:param str architecture: target architecture of the image
569571
:param str cname: canonical name of the target image
570572
:param str build_artifacts_dir: directory where the build artifacts are located
571573
"""
572574

573575
# TODO: construct oci_artifacts default data
574576

575-
oci_metadata = get_oci_metadata(cname, version, gardenlinux_root)
577+
# oci_metadata = get_oci_metadata(cname, version, architecture, gardenlinux_root)
576578

577579
manifest_image = oras.oci.NewManifest()
578580
total_size = 0
@@ -690,8 +692,7 @@ def create_layer(
690692
checksum_sha256 = calculate_sha256(file_path)
691693
layer = oras.oci.NewLayer(file_path, media_type, is_dir=False)
692694
layer["annotations"] = {
693-
oras.defaults.annotation_title: file_path,
694-
"application/vnd.gardenlinux.image.checksum.sha256": checksum_sha256,
695+
oras.defaults.annotation_title: os.path.basename(file_path),
695696
}
696697
self.sign_layer(
697698
layer, cname, version, architecture, checksum_sha256, media_type

tests/test_deduce_image_type.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from python_gardenlinux_lib.features.parse_features import (
2-
deduce_archive_filetype,
3-
deduce_image_filetype,
2+
deduce_archive_filetypes,
3+
deduce_image_filetypes,
44
)
55
import pytest
66

@@ -20,7 +20,7 @@
2020
],
2121
)
2222
def test_deduce_image_type(feature_name, expected_file_type):
23-
file_type = deduce_image_filetype(f"{GL_ROOT_DIR}/features/{feature_name}")
23+
file_type = deduce_image_filetypes(f"{GL_ROOT_DIR}/features/{feature_name}")
2424
assert sorted(expected_file_type) == file_type
2525

2626

@@ -35,5 +35,5 @@ def test_deduce_image_type(feature_name, expected_file_type):
3535
],
3636
)
3737
def test_deduce_archive_type(feature_name, expected_file_type):
38-
file_type = deduce_archive_filetype(f"{GL_ROOT_DIR}/features/{feature_name}")
38+
file_type = deduce_archive_filetypes(f"{GL_ROOT_DIR}/features/{feature_name}")
3939
assert sorted(expected_file_type) == file_type

0 commit comments

Comments
 (0)