Skip to content

Commit 6395199

Browse files
committed
Fixed machine comparisons via default variant
1 parent b9b3e34 commit 6395199

4 files changed

Lines changed: 51 additions & 62 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
[Unreleased]
9+
10+
### Changed
11+
12+
- Default-variant-safe `Platform` comparison (fixes linux/arm64/v8 not == linux/arm64)
13+
814
## [1.0] - 2023-08-10
915

1016
### Added

src/docker_export/__init__.py

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ def __repr__(self):
140140
value += f"/{self.variant}"
141141
return value
142142

143+
def __eq__(self, __value: object) -> bool:
144+
if not isinstance(__value, Platform):
145+
return False
146+
if self.os != __value.os or self.architecture != __value.architecture:
147+
return False
148+
default_variant: str = self.default_variant(self.architecture)
149+
return (self.variant or default_variant) == (__value.variant or default_variant)
150+
143151
@classmethod
144152
def parse(cls, platform_str: str) -> Platform:
145153
if platform_str == "auto":
@@ -161,6 +169,8 @@ def parse(cls, platform_str: str) -> Platform:
161169
architecture = "amd64"
162170
if architecture == "arm32":
163171
architecture = "arm"
172+
if architecture == "i386":
173+
architecture = "386"
164174

165175
if os not in ("linux", "windows"):
166176
raise ValueError(f"Invalid OS “{os}” from `{platform_str}`")
@@ -180,11 +190,6 @@ def parse(cls, platform_str: str) -> Platform:
180190
):
181191
raise ValueError(f"Invalid arch “{architecture}” from `{platform_str}`")
182192

183-
if architecture == "arm" and not variant:
184-
raise ValueError(
185-
f"Missing variant for arch “{architecture}” from `{platform_str}`"
186-
)
187-
188193
return cls(architecture=architecture, os=os, variant=variant)
189194

190195
@classmethod
@@ -204,7 +209,7 @@ def auto(cls):
204209
return cls.parse("linux/arm64/v8")
205210
elif machine.startswith("arm"):
206211
return cls.parse("linux/arm/v6")
207-
elif machine.startswith("i686") or machine.startswith("i386"):
212+
elif re.match(r"^i(3|5|6)86", machine):
208213
return cls.parse("linux/i386")
209214
return cls.parse("linux/amd64")
210215

@@ -216,18 +221,6 @@ def from_payload(cls, payload: dict[str, str]):
216221
variant=payload.get("variant", ""),
217222
)
218223

219-
def match(self, payload: dict[str, str]) -> bool:
220-
if self.variant and self.variant != (
221-
# allows matching linux/arm64 images with linux/arm/v8 requests
222-
payload.get("variant")
223-
or self.default_variant(payload.get("architecture", ""))
224-
):
225-
return False
226-
227-
return self.os == payload.get("os") and self.architecture == payload.get(
228-
"architecture"
229-
)
230-
231224

232225
@dataclass
233226
class Image:
@@ -431,7 +424,7 @@ def get_layers_from_v1_manifest(
431424
architecture = manifest.get("architecture", "amd64")
432425
os = manifest.get("os", "linux")
433426

434-
if not platform.match({"architecture": architecture, "os": os}):
427+
if platform != Platform(architecture=architecture, os=os, variant=""):
435428
raise ValueError(
436429
f"Requested platform ({platform}) is not available "
437430
f"for single-platform image {image}"
@@ -476,15 +469,16 @@ def get_layers_manifest(image: Image, platform: Platform, auth: RegistryAuth):
476469
manifest = fat_manifests
477470
else:
478471
# multi-platform image
472+
platforms: list[Platform] = []
479473
for arch_manifest in fat_manifests.get("manifests", []):
480-
if platform.match(arch_manifest.get("platform", {})):
474+
if not arch_manifest.get("platform"):
475+
continue
476+
manifest_platform = Platform.from_payload(arch_manifest["platform"])
477+
if platform == manifest_platform:
481478
manifest = get_layers_manifest_for(image, auth, arch_manifest["digest"])
479+
platforms.append(manifest_platform)
482480

483481
if not manifest:
484-
platforms = [
485-
Platform.from_payload(man.get("platform"))
486-
for man in fat_manifests.get("manifests", [])
487-
]
488482
raise V2ImageNotFoundError(image, platform, platforms)
489483

490484
logger.debug(f"layers_manifest={format_json(manifest)}")
@@ -778,16 +772,15 @@ def get_image_digest(image: Image, platform: Platform) -> str:
778772
if platform != platform.default():
779773
raise V1ImageNotFoundError(image=image, platform=platform)
780774
return fat_manifests["config"]["digest"]
781-
else:
782-
# multi-platform image
783-
platforms: list[Platform] = []
784-
for arch_manifest in fat_manifests.get("manifests", []):
785-
if not arch_manifest.get("platform"):
786-
continue
787-
manifest_platform = Platform.from_payload(arch_manifest["platform"])
788-
if platform == manifest_platform:
789-
return arch_manifest["digest"]
790-
platforms.append(manifest_platform)
775+
# multi-platform image
776+
platforms: list[Platform] = []
777+
for arch_manifest in fat_manifests.get("manifests", []):
778+
if not arch_manifest.get("platform"):
779+
continue
780+
manifest_platform = Platform.from_payload(arch_manifest["platform"])
781+
if platform == manifest_platform:
782+
return arch_manifest["digest"]
783+
platforms.append(manifest_platform)
791784

792785
raise V2ImageNotFoundError(image=image, platform=platform, platforms=platforms)
793786

tests/test_digest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
"linux/arm64",
3434
"sha256:f7d859179210c4407447e4e37e401cefc831dfe3e686b66988a225417db884c8",
3535
),
36+
(
37+
f"{kiwix_tools}:3.5.0-2",
38+
"linux/arm64/v8",
39+
"sha256:f7d859179210c4407447e4e37e401cefc831dfe3e686b66988a225417db884c8",
40+
),
3641
(
3742
f"{kiwix_tools}:3.5.0-2",
3843
"linux/armv7",

tests/test_platform.py

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from __future__ import annotations
22

3-
from typing import Any
4-
53
import pytest
64

75
from docker_export import Platform
@@ -54,32 +52,19 @@ def test_from_payload():
5452

5553

5654
@pytest.mark.parametrize(
57-
"value_a, payload, should_match",
55+
"value_a, value_b, should_match",
5856
[
59-
("linux/amd64", {"architecture": "linux", "os": "amd64"}, False),
60-
("linux/amd64", {"architecture": "amd64", "os": "linux"}, True),
61-
(
62-
"linux/amd64",
63-
{"architecture": "amd64", "os": "linux", "variant": "v3"},
64-
True,
65-
),
66-
("linux/armv6", {"architecture": "arm", "os": "linux", "variant": "v6"}, True),
67-
("linux/armv6", {"architecture": "arm", "os": "linux"}, False),
68-
("linux/armv6", {"architecture": "arm", "os": "linux", "variant": "v7"}, False),
69-
("linux/armv6", {"architecture": "arm", "os": "linux", "variant": "v8"}, False),
70-
("linux/armv7", {"architecture": "arm", "os": "linux"}, True),
71-
("linux/armv7", {"architecture": "arm", "os": "linux", "variant": "v7"}, True),
72-
(
73-
"linux/arm64/v8",
74-
{"architecture": "arm64", "os": "linux", "variant": "v8"},
75-
True,
76-
),
77-
(
78-
"linux/arm64",
79-
{"architecture": "arm64", "os": "linux", "variant": "v8"},
80-
True,
81-
),
57+
("linux/amd64", "linux/amd64", True),
58+
("linux/amd64", "linux/amd64/v3", False),
59+
("linux/armv6", "linux/arm", False),
60+
("linux/armv7", "linux/arm", True),
61+
("linux/armv6", "linux/arm/v6", True),
62+
("linux/armv6", "linux/arm/v7", False),
63+
("linux/armv6", "linux/arm/v8", False),
64+
("linux/armv7", "linux/arm/v7", True),
65+
("linux/arm64/v8", "linux/arm64/v8", True),
66+
("linux/arm64", "linux/arm64/v8", True),
8267
],
8368
)
84-
def test_match(*, value_a: str, payload: dict[str, Any], should_match: bool):
85-
assert Platform.parse(value_a).match(payload) is should_match
69+
def test_match(*, value_a: str, value_b: str, should_match: bool):
70+
assert (Platform.parse(value_a) == Platform.parse(value_b)) is should_match

0 commit comments

Comments
 (0)