Skip to content

Commit 01c7621

Browse files
committed
update gardenlinux.oci.registry.update_index
enables pushing additional tags to OCI index
1 parent bf729fa commit 01c7621

File tree

4 files changed

+157
-64
lines changed

4 files changed

+157
-64
lines changed

src/gardenlinux/oci/__main__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,21 @@ def push_manifest(
107107
default=False,
108108
help="Use HTTP to communicate with the registry",
109109
)
110-
def update_index(container, version, manifest_folder, insecure):
110+
@click.option(
111+
"--additional_tag",
112+
required=False,
113+
multiple=True,
114+
help="Additional tag to push the index with",
115+
)
116+
def update_index(container, version, manifest_folder, insecure, additional_tag):
111117
"""push a index entry from a list of files to an index"""
112118
container_name = f"{container}:{version}"
113119
registry = GlociRegistry(
114120
container_name=container_name,
115121
token=os.getenv("GL_CLI_REGISTRY_TOKEN"),
116122
insecure=insecure,
117123
)
118-
registry.update_index(manifest_folder)
124+
registry.update_index(manifest_folder, additional_tag)
119125

120126

121127
def main():

src/gardenlinux/oci/registry.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,9 +601,12 @@ def push_image_manifest(
601601

602602
return local_digest
603603

604-
def update_index(self, manifest_folder):
604+
def update_index(self, manifest_folder, additional_tags: list = None):
605605
"""
606606
replaces an old manifest entry with a new manifest entry
607+
608+
:param str manifest_folder: the folder where the manifest entries are read from
609+
:param list additional_tags: the additional tags to push the index with
607610
"""
608611
index = self.get_index()
609612
# Ensure mediaType is set for existing indices
@@ -636,6 +639,12 @@ def update_index(self, manifest_folder):
636639
self._check_200_response(self.upload_index(index))
637640
logger.info(f"Index pushed with {new_entries} new entries")
638641

642+
for tag in additional_tags:
643+
self.container.digest = None
644+
self.container.tag = tag
645+
self.upload_index(index)
646+
logger.info(f"Index pushed with additional tag {tag}")
647+
639648
def create_layer(
640649
self,
641650
file_path: str,

tests/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919
TEST_FEATURE_SET = "_slim,base,container"
2020
TEST_COMMIT = get_gardenlinux_commit(GL_ROOT_DIR, 8)
2121
TEST_VERSION = "1000.0"
22+
TEST_VERSION_STABLE = "1000"

tests/test_oci.py

Lines changed: 138 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
TEST_COMMIT,
2424
TEST_FEATURE_SET,
2525
TEST_VERSION,
26+
TEST_VERSION_STABLE,
2627
TEST_PLATFORMS,
2728
TEST_FEATURE_STRINGS_SHORT,
2829
TEST_ARCHITECTURES,
@@ -53,7 +54,6 @@ def push_manifest(runner, version, arch, cname, additional_tags=None):
5354
"manifests/manifest.json",
5455
]
5556

56-
# Add additional tags if provided
5757
if additional_tags:
5858
for tag in additional_tags:
5959
cmd.extend(["--additional_tag", tag])
@@ -71,23 +71,35 @@ def push_manifest(runner, version, arch, cname, additional_tags=None):
7171
return False
7272

7373

74-
def update_index(runner, version):
75-
"""Update index in registry"""
74+
def update_index(runner, version, additional_tags=None):
75+
"""Update index in registry and return success status"""
7676
print("Updating index")
77-
result = runner.invoke(
78-
gl_oci,
79-
[
80-
"update-index",
81-
"--container",
82-
CONTAINER_NAME_ZOT_EXAMPLE,
83-
"--version",
84-
version,
85-
"--insecure",
86-
"True",
87-
],
88-
catch_exceptions=False,
89-
)
90-
print(f"Update index output: {result.output}")
77+
78+
cmd = [
79+
"update-index",
80+
"--container",
81+
CONTAINER_NAME_ZOT_EXAMPLE,
82+
"--version",
83+
version,
84+
"--insecure",
85+
"True",
86+
]
87+
88+
if additional_tags:
89+
for tag in additional_tags:
90+
cmd.extend(["--additional_tag", tag])
91+
92+
try:
93+
result = runner.invoke(
94+
gl_oci,
95+
cmd,
96+
catch_exceptions=False,
97+
)
98+
print(f"Update index output: {result.output}")
99+
return result.exit_code == 0
100+
except Exception as e:
101+
print(f"Error during update index: {str(e)}")
102+
return False
91103

92104

93105
def get_catalog(client):
@@ -181,14 +193,84 @@ def verify_combined_tag_manifest(manifest, arch, cname, version, feature_set, co
181193
), f"Manifest should have commit {commit}"
182194

183195

196+
def verify_additional_tags(
197+
client, repo, additional_tags, reference_digest=None, fail_on_missing=True
198+
):
199+
"""
200+
Verify that all additional tags exist and match the reference digest if provided.
201+
202+
Args:
203+
client: Reggie client
204+
repo: Repository name
205+
additional_tags: List of tags to verify
206+
reference_digest: Optional digest to compare against
207+
fail_on_missing: If True, fail the test when tags are missing
208+
209+
Returns:
210+
List of missing tags
211+
"""
212+
missing_tags = []
213+
214+
for tag in additional_tags:
215+
print(f"Verifying additional tag: {tag}")
216+
try:
217+
# Create a simple request for the manifest
218+
tag_req = client.NewRequest("GET", f"/v2/{repo}/manifests/{tag}")
219+
tag_req.headers.update(
220+
{
221+
"Accept": "application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.index.v1+json"
222+
}
223+
)
224+
225+
tag_resp = client.Do(tag_req)
226+
227+
if tag_resp.status_code != 200:
228+
print(
229+
f"✗ Could not find additional tag {tag}: status {tag_resp.status_code}"
230+
)
231+
missing_tags.append(tag)
232+
continue
233+
234+
# Get the digest
235+
digest = tag_resp.headers.get("Docker-Content-Digest")
236+
237+
# Check digest if reference provided
238+
if reference_digest and digest != reference_digest:
239+
print(
240+
f"✗ Tag {tag} has different digest: {digest} (expected {reference_digest})"
241+
)
242+
missing_tags.append(tag)
243+
continue
244+
245+
print(f"✓ Successfully verified additional tag {tag} with digest: {digest}")
246+
247+
except Exception as e:
248+
print(f"✗ Error verifying tag {tag}: {str(e)}")
249+
missing_tags.append(tag)
250+
251+
# If any tags are missing and fail_on_missing is True, fail the test
252+
if missing_tags and fail_on_missing:
253+
missing_tags_str = "\n - ".join(missing_tags)
254+
pytest.fail(f"Missing tags:\n - {missing_tags_str}")
255+
256+
return missing_tags
257+
258+
184259
@pytest.mark.usefixtures("zot_session")
185260
@pytest.mark.parametrize(
186-
"version, cname, arch, additional_tags",
261+
"version, cname, arch, additional_tags_index, additional_tags_manifest",
187262
[
188263
(
189264
TEST_VERSION,
190265
f"{platform}-{feature_string}",
191266
arch,
267+
[
268+
f"{TEST_VERSION}-patch",
269+
f"{TEST_VERSION}-patch-{TEST_COMMIT}",
270+
f"{TEST_VERSION_STABLE}",
271+
f"{TEST_VERSION_STABLE}-stable",
272+
f"latest",
273+
],
192274
[
193275
f"{TEST_VERSION}-patch-{platform}-{feature_string}-{arch}",
194276
f"{TEST_VERSION}-{TEST_COMMIT}-patch-{platform}-{feature_string}-{arch}",
@@ -200,19 +282,24 @@ def verify_combined_tag_manifest(manifest, arch, cname, version, feature_set, co
200282
for arch in TEST_ARCHITECTURES
201283
],
202284
)
203-
def test_push_manifest_and_index(version, arch, cname, additional_tags):
285+
def test_push_manifest_and_index(
286+
version, arch, cname, additional_tags_index, additional_tags_manifest
287+
):
204288
print(f"\n\n=== Starting test for {cname} {arch} {version} ===")
205289
runner = CliRunner()
206290
registry_url = "http://127.0.0.1:18081"
207291
repo_name = "gardenlinux-example"
208292
combined_tag = f"{version}-{cname}-{arch}"
209293

210294
# Push manifest and update index
211-
push_successful = push_manifest(runner, version, arch, cname, additional_tags)
295+
push_successful = push_manifest(
296+
runner, version, arch, cname, additional_tags_manifest
297+
)
212298
assert push_successful, "Manifest push should succeed"
213299

214300
if push_successful:
215-
update_index(runner, version)
301+
update_index_successful = update_index(runner, version, additional_tags_index)
302+
assert update_index_successful, "Index update should succeed"
216303

217304
# Verify registry contents
218305
print(f"\n=== Verifying registry for {cname} {arch} {version} ===")
@@ -234,54 +321,44 @@ def test_push_manifest_and_index(version, arch, cname, additional_tags):
234321
tags = get_tags(client, repo_name)
235322
print(f"Tags for {repo_name}: {tags}")
236323

237-
# Verify version tag (index)
238-
if version in tags:
239-
print(f"\nVerifying index with tag {version}...")
240-
index_manifest, index_digest = get_manifest(client, repo_name, version)
241-
print(f"Successfully retrieved index with digest: {index_digest}")
242-
verify_index_manifest(index_manifest, arch)
243-
else:
244-
pytest.fail(f"Tag {version} not found in repository {repo_name}")
245-
246-
# Verify combined tag
324+
# FIRST: Verify manifest with combined tag (the actual artifact)
325+
print(f"\n=== Verifying manifest with combined tag {combined_tag} ===")
247326
if combined_tag in tags:
248-
print(f"\nVerifying manifest with combined tag {combined_tag}...")
249-
combined_manifest, combined_digest = get_manifest(
250-
client, repo_name, combined_tag
251-
)
252-
print(f"Successfully retrieved manifest with digest: {combined_digest}")
327+
manifest, manifest_digest = get_manifest(client, repo_name, combined_tag)
328+
print(f"Successfully retrieved manifest with digest: {manifest_digest}")
253329
verify_combined_tag_manifest(
254-
combined_manifest, arch, cname, version, TEST_FEATURE_SET, TEST_COMMIT
330+
manifest, arch, cname, version, TEST_FEATURE_SET, TEST_COMMIT
331+
)
332+
333+
# Verify additional tags for manifest
334+
print("\n=== Verifying additional tags for manifest ===")
335+
verify_additional_tags(
336+
client,
337+
repo_name,
338+
additional_tags_manifest,
339+
reference_digest=manifest_digest,
340+
fail_on_missing=True,
255341
)
256342
else:
257343
pytest.fail(f"Combined tag {combined_tag} not found in repository {repo_name}")
258344

259-
print("\n=== Verifying additional tags in main repository ===")
260-
# Force update the tags list to ensure it's current
261-
updated_tags = get_tags(client, repo_name)
262-
print(f"Updated tags for {repo_name}: {updated_tags}")
263-
264-
# Now try each additional tag but don't fail the test if not found
265-
missing_tags = []
266-
for tag in additional_tags:
267-
print(f"Verifying additional tag: {tag}")
268-
try:
269-
tag_manifest, tag_digest = get_manifest(client, repo_name, tag)
270-
print(
271-
f"✓ Successfully retrieved additional tag {tag} with digest: {tag_digest}"
272-
)
273-
except Exception as e:
274-
print(f"✗ Could not find additional tag {tag}: {str(e)}")
275-
missing_tags.append(tag)
345+
# SECOND: Verify index (the collection of manifests)
346+
print(f"\n=== Verifying index with tag {version} ===")
347+
if version in tags:
348+
index_manifest, index_digest = get_manifest(client, repo_name, version)
349+
print(f"Successfully retrieved index with digest: {index_digest}")
350+
verify_index_manifest(index_manifest, arch)
276351

277-
# Report missing tags but don't fail the test
278-
if missing_tags:
279-
print(
280-
f"\nWarning: {len(missing_tags)} additional tags were not found in the registry:"
352+
# Verify additional tags for index
353+
print("\n=== Verifying additional tags for index ===")
354+
verify_additional_tags(
355+
client,
356+
repo_name,
357+
additional_tags_index,
358+
reference_digest=index_digest,
359+
fail_on_missing=True,
281360
)
282-
for tag in missing_tags:
283-
print(f" - {tag}")
284361
else:
285-
print("\nAll additional tags were successfully pushed!")
362+
pytest.fail(f"Tag {version} not found in repository {repo_name}")
286363

287364
print("\n=== Registry verification completed ===")

0 commit comments

Comments
 (0)