1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
package libimage
import (
"context"
"fmt"
"runtime"
"github.com/containerd/containerd/platforms"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
// PlatformPolicy controls the behavior of image-platform matching.
type PlatformPolicy int
const (
// Only debug log if an image does not match the expected platform.
PlatformPolicyDefault PlatformPolicy = iota
// Warn if an image does not match the expected platform.
PlatformPolicyWarn
)
// NormalizePlatform normalizes (according to the OCI spec) the specified os,
// arch and variant. If left empty, the individual item will be normalized.
func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant string) {
platformSpec := v1.Platform{
OS: rawOS,
Architecture: rawArch,
Variant: rawVariant,
}
normalizedSpec := platforms.Normalize(platformSpec)
if normalizedSpec.Variant == "" && rawVariant != "" {
normalizedSpec.Variant = rawVariant
}
rawPlatform := toPlatformString(normalizedSpec.OS, normalizedSpec.Architecture, normalizedSpec.Variant)
normalizedPlatform, err := platforms.Parse(rawPlatform)
if err != nil {
logrus.Debugf("Error normalizing platform: %v", err)
return rawOS, rawArch, rawVariant
}
logrus.Debugf("Normalized platform %s to %s", rawPlatform, normalizedPlatform)
os = rawOS
if rawOS != "" {
os = normalizedPlatform.OS
}
arch = rawArch
if rawArch != "" {
arch = normalizedPlatform.Architecture
}
variant = rawVariant
if rawVariant != "" || (rawVariant == "" && normalizedPlatform.Variant != "") {
variant = normalizedPlatform.Variant
}
return os, arch, variant
}
func toPlatformString(os, arch, variant string) string {
if os == "" {
os = runtime.GOOS
}
if arch == "" {
arch = runtime.GOARCH
}
if variant == "" {
return fmt.Sprintf("%s/%s", os, arch)
}
return fmt.Sprintf("%s/%s/%s", os, arch, variant)
}
// Checks whether the image matches the specified platform.
// Returns
// * 1) a matching error that can be used for logging (or returning) what does not match
// * 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error)
// * 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.)
func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) (error, bool, error) {
if err := i.isCorrupted(""); err != nil {
return err, false, nil
}
inspectInfo, err := i.inspectInfo(ctx)
if err != nil {
return nil, false, fmt.Errorf("inspecting image: %w", err)
}
customPlatform := len(os)+len(arch)+len(variant) != 0
expected, err := platforms.Parse(toPlatformString(os, arch, variant))
if err != nil {
return nil, false, fmt.Errorf("parsing host platform: %v", err)
}
fromImage, err := platforms.Parse(toPlatformString(inspectInfo.Os, inspectInfo.Architecture, inspectInfo.Variant))
if err != nil {
return nil, false, fmt.Errorf("parsing image platform: %v", err)
}
if platforms.NewMatcher(expected).Match(fromImage) {
return nil, customPlatform, nil
}
return fmt.Errorf("image platform (%s) does not match the expected platform (%s)", fromImage, expected), customPlatform, nil
}
|