Skip to content

Commit 76e1a74

Browse files
committed
update gardenlinux.oci.registry.update_index
enables pushing additional tags to OCI index
1 parent a28a446 commit 76e1a74

File tree

4 files changed

+139
-66
lines changed

4 files changed

+139
-66
lines changed

src/gardenlinux/oci/__main__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +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)
119-
124+
registry.update_index(manifest_folder, additional_tag)
120125

121126
def main():
122127
"""Entry point for the gl-oci command."""

src/gardenlinux/oci/registry.py

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

601601
return local_digest
602602

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

641+
for tag in additional_tags:
642+
self.container.digest = None
643+
self.container.tag = tag
644+
self.upload_index(index)
645+
logger.info(f"Index pushed with additional tag {tag}")
646+
638647
def create_layer(
639648
self,
640649
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: 120 additions & 62 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,24 +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}")
9177

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
92103

93104
def get_catalog(client):
94105
"""Get catalog from registry and return repositories list"""
@@ -181,14 +192,78 @@ def verify_combined_tag_manifest(manifest, arch, cname, version, feature_set, co
181192
), f"Manifest should have commit {commit}"
182193

183194

195+
def verify_additional_tags(client, repo, additional_tags, reference_digest=None, fail_on_missing=True):
196+
"""
197+
Verify that all additional tags exist and match the reference digest if provided.
198+
199+
Args:
200+
client: Reggie client
201+
repo: Repository name
202+
additional_tags: List of tags to verify
203+
reference_digest: Optional digest to compare against
204+
fail_on_missing: If True, fail the test when tags are missing
205+
206+
Returns:
207+
List of missing tags
208+
"""
209+
missing_tags = []
210+
211+
for tag in additional_tags:
212+
print(f"Verifying additional tag: {tag}")
213+
try:
214+
# Create a simple request for the manifest
215+
tag_req = client.NewRequest("GET", f"/v2/{repo}/manifests/{tag}")
216+
tag_req.headers.update(
217+
{
218+
"Accept": "application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.index.v1+json"
219+
}
220+
)
221+
222+
tag_resp = client.Do(tag_req)
223+
224+
if tag_resp.status_code != 200:
225+
print(f"✗ Could not find additional tag {tag}: status {tag_resp.status_code}")
226+
missing_tags.append(tag)
227+
continue
228+
229+
# Get the digest
230+
digest = tag_resp.headers.get("Docker-Content-Digest")
231+
232+
# Check digest if reference provided
233+
if reference_digest and digest != reference_digest:
234+
print(f"✗ Tag {tag} has different digest: {digest} (expected {reference_digest})")
235+
missing_tags.append(tag)
236+
continue
237+
238+
print(f"✓ Successfully verified additional tag {tag} with digest: {digest}")
239+
240+
except Exception as e:
241+
print(f"✗ Error verifying tag {tag}: {str(e)}")
242+
missing_tags.append(tag)
243+
244+
# If any tags are missing and fail_on_missing is True, fail the test
245+
if missing_tags and fail_on_missing:
246+
missing_tags_str = "\n - ".join(missing_tags)
247+
pytest.fail(f"Missing tags:\n - {missing_tags_str}")
248+
249+
return missing_tags
250+
251+
184252
@pytest.mark.usefixtures("zot_session")
185253
@pytest.mark.parametrize(
186-
"version, cname, arch, additional_tags",
254+
"version, cname, arch, additional_tags_index, additional_tags_manifest",
187255
[
188256
(
189257
TEST_VERSION,
190258
f"{platform}-{feature_string}",
191259
arch,
260+
[
261+
f"{TEST_VERSION}-patch",
262+
f"{TEST_VERSION}-patch-{TEST_COMMIT}",
263+
f"{TEST_VERSION_STABLE}",
264+
f"{TEST_VERSION_STABLE}-stable",
265+
f"latest",
266+
],
192267
[
193268
f"{TEST_VERSION}-patch-{platform}-{feature_string}-{arch}",
194269
f"{TEST_VERSION}-{TEST_COMMIT}-patch-{platform}-{feature_string}-{arch}",
@@ -200,19 +275,20 @@ def verify_combined_tag_manifest(manifest, arch, cname, version, feature_set, co
200275
for arch in TEST_ARCHITECTURES
201276
],
202277
)
203-
def test_push_manifest_and_index(version, arch, cname, additional_tags):
278+
def test_push_manifest_and_index(version, arch, cname, additional_tags_index, additional_tags_manifest):
204279
print(f"\n\n=== Starting test for {cname} {arch} {version} ===")
205280
runner = CliRunner()
206281
registry_url = "http://127.0.0.1:18081"
207282
repo_name = "gardenlinux-example"
208283
combined_tag = f"{version}-{cname}-{arch}"
209284

210285
# Push manifest and update index
211-
push_successful = push_manifest(runner, version, arch, cname, additional_tags)
286+
push_successful = push_manifest(runner, version, arch, cname, additional_tags_manifest)
212287
assert push_successful, "Manifest push should succeed"
213288

214289
if push_successful:
215-
update_index(runner, version)
290+
update_index_successful = update_index(runner, version, additional_tags_index)
291+
assert update_index_successful, "Index update should succeed"
216292

217293
# Verify registry contents
218294
print(f"\n=== Verifying registry for {cname} {arch} {version} ===")
@@ -234,54 +310,36 @@ def test_push_manifest_and_index(version, arch, cname, additional_tags):
234310
tags = get_tags(client, repo_name)
235311
print(f"Tags for {repo_name}: {tags}")
236312

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
313+
# FIRST: Verify manifest with combined tag (the actual artifact)
314+
print(f"\n=== Verifying manifest with combined tag {combined_tag} ===")
247315
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}")
316+
manifest, manifest_digest = get_manifest(client, repo_name, combined_tag)
317+
print(f"Successfully retrieved manifest with digest: {manifest_digest}")
253318
verify_combined_tag_manifest(
254-
combined_manifest, arch, cname, version, TEST_FEATURE_SET, TEST_COMMIT
319+
manifest, arch, cname, version, TEST_FEATURE_SET, TEST_COMMIT
255320
)
321+
322+
# Verify additional tags for manifest
323+
print("\n=== Verifying additional tags for manifest ===")
324+
verify_additional_tags(client, repo_name, additional_tags_manifest,
325+
reference_digest=manifest_digest,
326+
fail_on_missing=True)
256327
else:
257328
pytest.fail(f"Combined tag {combined_tag} not found in repository {repo_name}")
258329

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)
330+
# SECOND: Verify index (the collection of manifests)
331+
print(f"\n=== Verifying index with tag {version} ===")
332+
if version in tags:
333+
index_manifest, index_digest = get_manifest(client, repo_name, version)
334+
print(f"Successfully retrieved index with digest: {index_digest}")
335+
verify_index_manifest(index_manifest, arch)
276336

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:"
281-
)
282-
for tag in missing_tags:
283-
print(f" - {tag}")
337+
# Verify additional tags for index
338+
print("\n=== Verifying additional tags for index ===")
339+
verify_additional_tags(client, repo_name, additional_tags_index,
340+
reference_digest=index_digest,
341+
fail_on_missing=True)
284342
else:
285-
print("\nAll additional tags were successfully pushed!")
343+
pytest.fail(f"Tag {version} not found in repository {repo_name}")
286344

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

0 commit comments

Comments
 (0)