package libimage import ( "context" "fmt" "runtime" "github.com/containerd/containerd/platforms" "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 not be normalized. func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant string) { rawPlatform := toPlatformString(rawOS, rawArch, rawVariant) 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 != "" { 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) { 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 }