Skip to content

Commit 513920c

Browse files
committed
feat(sync): Add support for platform/architecture
1 parent e74cd8f commit 513920c

File tree

6 files changed

+552
-46
lines changed

6 files changed

+552
-46
lines changed

examples/config-sync-architectures.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"retryDelay": "5m",
2828
"onlySigned": true,
2929
"architectures": ["amd64", "arm64"],
30+
"platforms": ["linux/amd64", "linux/arm64"],
3031
"content": [
3132
{
3233
"prefix": "/repo1/repo",
@@ -60,6 +61,7 @@
6061
"tlsVerify": false,
6162
"onDemand": false,
6263
"architectures": ["amd64"],
64+
"platforms": ["linux/amd64", "windows/amd64"],
6365
"content": [
6466
{
6567
"prefix": "**",
@@ -77,7 +79,8 @@
7779
"tlsVerify": true,
7880
"maxRetries": 5,
7981
"retryDelay": "30s",
80-
"architectures": ["amd64", "arm64", "arm"]
82+
"architectures": ["amd64", "arm64", "arm"],
83+
"platforms": ["linux/amd64", "linux/arm64", "linux/arm"]
8184
},
8285
{
8386
"urls": [
@@ -92,7 +95,16 @@
9295
"onDemand": true,
9396
"tlsVerify": true,
9497
"maxRetries": 5,
95-
"retryDelay": "1m"
98+
"retryDelay": "1m",
99+
"platforms": ["darwin/amd64", "linux/amd64", "linux/arm64"]
100+
},
101+
{
102+
"urls": [
103+
"https://registry5:5000"
104+
],
105+
"onDemand": false,
106+
"tlsVerify": true,
107+
"platforms": ["linux/amd64", "amd64", "arm64"]
96108
}
97109
]
98110
}

examples/config-sync-platforms.json

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{
2+
"distSpecVersion": "1.1.1",
3+
"storage": {
4+
"rootDirectory": "/tmp/zot"
5+
},
6+
"http": {
7+
"address": "127.0.0.1",
8+
"port": "8080"
9+
},
10+
"log": {
11+
"level": "debug"
12+
},
13+
"extensions": {
14+
"sync": {
15+
"enable": true,
16+
"credentialsFile": "./examples/sync-auth-filepath.json",
17+
"registries": [
18+
{
19+
"urls": [
20+
"https://registry1:5000"
21+
],
22+
"onDemand": false,
23+
"pollInterval": "6h",
24+
"tlsVerify": true,
25+
"certDir": "/home/user/certs",
26+
"maxRetries": 3,
27+
"retryDelay": "5m",
28+
"onlySigned": true,
29+
"platforms": ["linux/amd64", "linux/arm64", "windows/amd64"],
30+
"content": [
31+
{
32+
"prefix": "/repo1/repo",
33+
"tags": {
34+
"regex": "4.*",
35+
"semver": true
36+
}
37+
},
38+
{
39+
"prefix": "/repo2/repo",
40+
"destination": "/repo",
41+
"stripPrefix": true
42+
},
43+
{
44+
"prefix": "/repo3/**"
45+
},
46+
{
47+
"prefix": "/repo4/**",
48+
"tags": {
49+
"excludeRegex": ".*-(amd64|arm64)$"
50+
}
51+
}
52+
]
53+
},
54+
{
55+
"urls": [
56+
"https://registry2:5000",
57+
"https://registry3:5000"
58+
],
59+
"pollInterval": "12h",
60+
"tlsVerify": false,
61+
"onDemand": false,
62+
"platforms": ["linux/amd64"],
63+
"content": [
64+
{
65+
"prefix": "**",
66+
"tags": {
67+
"semver": true
68+
}
69+
}
70+
]
71+
},
72+
{
73+
"urls": [
74+
"https://index.docker.io"
75+
],
76+
"onDemand": true,
77+
"tlsVerify": true,
78+
"maxRetries": 5,
79+
"retryDelay": "30s",
80+
"architectures": ["amd64", "arm64", "arm"]
81+
},
82+
{
83+
"urls": [
84+
"https://demo.goharbor.io"
85+
],
86+
"pollInterval": "12h",
87+
"content": [
88+
{
89+
"prefix": "zot/**"
90+
}
91+
],
92+
"onDemand": true,
93+
"tlsVerify": true,
94+
"maxRetries": 5,
95+
"retryDelay": "1m",
96+
"platforms": ["darwin/amd64", "linux/amd64", "linux/arm64"]
97+
},
98+
{
99+
"urls": [
100+
"https://registry4:5000"
101+
],
102+
"onDemand": false,
103+
"tlsVerify": true,
104+
"platforms": ["linux/amd64", "amd64", "arm64"]
105+
}
106+
]
107+
}
108+
}
109+
}

pkg/extensions/config/sync/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ type RegistryConfig struct {
3434
OnlySigned *bool
3535
CredentialHelper string
3636
PreserveDigest bool // sync without converting
37-
Architectures []string // filter architectures during sync
37+
Architectures []string `mapstructure:",omitempty"` // filter architectures during sync (DEPRECATED: use Platforms instead)
38+
Platforms []string `mapstructure:",omitempty"` // filter platforms during sync (supports both "arch" and "os/arch" formats)
3839
}
3940

4041
type Content struct {

pkg/extensions/sync/destination.go

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,58 @@ import (
3030
storageTypes "zotregistry.dev/zot/pkg/storage/types"
3131
)
3232

33+
// Platform represents an OS/architecture combination
34+
type Platform struct {
35+
OS string
36+
Architecture string
37+
}
38+
39+
// ParsePlatform parses a platform string into a Platform struct
40+
// The string can be in the format "os/arch" or just "arch"
41+
func ParsePlatform(platform string) Platform {
42+
parts := strings.Split(platform, "/")
43+
if len(parts) == 2 {
44+
return Platform{
45+
OS: parts[0],
46+
Architecture: parts[1],
47+
}
48+
}
49+
// If only one part, assume it's the architecture
50+
return Platform{
51+
OS: "",
52+
Architecture: platform,
53+
}
54+
}
55+
56+
// MatchesPlatform checks if the given platform matches any of the platform specifications
57+
// Platform specs can be in format "os/arch" or just "arch"
58+
func MatchesPlatform(platform *ispec.Platform, platformSpecs []string) bool {
59+
if platform == nil || len(platformSpecs) == 0 {
60+
return true
61+
}
62+
63+
for _, spec := range platformSpecs {
64+
specPlatform := ParsePlatform(spec)
65+
66+
// Check if architecture matches
67+
if specPlatform.Architecture != "" &&
68+
specPlatform.Architecture != platform.Architecture {
69+
continue
70+
}
71+
72+
// Check if OS matches (if specified)
73+
if specPlatform.OS != "" &&
74+
specPlatform.OS != platform.OS {
75+
continue
76+
}
77+
78+
// If we got here, it's a match
79+
return true
80+
}
81+
82+
return false
83+
}
84+
3385
type DestinationRegistry struct {
3486
storeController storage.StoreController
3587
tempStorage OciLayoutStorage
@@ -236,33 +288,46 @@ func (registry *DestinationRegistry) copyManifest(repo string, desc ispec.Descri
236288
return err
237289
}
238290

239-
// Filter manifests based on architectures if configured
291+
// Filter manifests based on platforms/architectures if configured
240292
var filteredManifests []ispec.Descriptor
241-
if registry.config != nil && len(registry.config.Architectures) > 0 {
242-
registry.log.Info().
243-
Strs("architectures", registry.config.Architectures).
244-
Str("repository", repo).
245-
Str("reference", reference).
246-
Msg("filtering manifest list by architectures")
247293

248-
for _, manifest := range indexManifest.Manifests {
249-
if manifest.Platform != nil && manifest.Platform.Architecture != "" {
250-
// Check if this architecture should be included
251-
shouldInclude := false
252-
for _, configArch := range registry.config.Architectures {
253-
if manifest.Platform.Architecture == configArch {
254-
shouldInclude = true
255-
break
256-
}
257-
}
294+
// Determine which platform specifications to use
295+
var platformSpecs []string
296+
if registry.config != nil {
297+
if len(registry.config.Platforms) > 0 {
298+
platformSpecs = registry.config.Platforms
299+
registry.log.Info().
300+
Strs("platforms", registry.config.Platforms).
301+
Str("repository", repo).
302+
Str("reference", reference).
303+
Msg("filtering manifest list by platforms")
304+
} else if len(registry.config.Architectures) > 0 {
305+
platformSpecs = registry.config.Architectures
306+
registry.log.Info().
307+
Strs("architectures", registry.config.Architectures).
308+
Str("repository", repo).
309+
Str("reference", reference).
310+
Msg("filtering manifest list by architectures (deprecated)")
311+
}
312+
}
258313

259-
if shouldInclude {
314+
// Apply filtering if we have platform specifications
315+
if len(platformSpecs) > 0 {
316+
for _, manifest := range indexManifest.Manifests {
317+
if manifest.Platform != nil {
318+
// Check if this platform should be included
319+
if MatchesPlatform(manifest.Platform, platformSpecs) {
260320
filteredManifests = append(filteredManifests, manifest)
261321
} else {
322+
platformDesc := manifest.Platform.Architecture
323+
if manifest.Platform.OS != "" {
324+
platformDesc = manifest.Platform.OS + "/" + manifest.Platform.Architecture
325+
}
326+
262327
registry.log.Info().
263328
Str("repository", repo).
264-
Str("architecture", manifest.Platform.Architecture).
265-
Msg("skipping architecture during sync")
329+
Str("platform", platformDesc).
330+
Msg("skipping platform during sync")
266331
}
267332
} else {
268333
// No platform info, include the manifest
@@ -275,7 +340,7 @@ func (registry *DestinationRegistry) copyManifest(repo string, desc ispec.Descri
275340
registry.log.Warn().
276341
Str("repository", repo).
277342
Str("reference", reference).
278-
Msg("no architecture matched the configured filters, manifest list might be empty")
343+
Msg("no platform matched the configured filters, manifest list might be empty")
279344
}
280345
} else {
281346
// No filtering, use all manifests
@@ -311,7 +376,8 @@ func (registry *DestinationRegistry) copyManifest(repo string, desc ispec.Descri
311376
}
312377

313378
// If we've filtered the manifest list, we need to update it
314-
if registry.config != nil && len(registry.config.Architectures) > 0 &&
379+
if registry.config != nil &&
380+
(len(registry.config.Architectures) > 0 || len(registry.config.Platforms) > 0) &&
315381
len(filteredManifests) != len(indexManifest.Manifests) && len(filteredManifests) > 0 {
316382
// Create a new index with the filtered manifests
317383
indexManifest.Manifests = filteredManifests

0 commit comments

Comments
 (0)