build(deps): bump github.com/containerd/platforms

Bumps [github.com/containerd/platforms](https://github.com/containerd/platforms) from 1.0.0-rc.2 to 1.0.0-rc.3.
- [Release notes](https://github.com/containerd/platforms/releases)
- [Commits](https://github.com/containerd/platforms/compare/v1.0.0-rc.2...v1.0.0-rc.3)

---
updated-dependencies:
- dependency-name: github.com/containerd/platforms
  dependency-version: 1.0.0-rc.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2026-03-23 23:24:16 +00:00
committed by GitHub
parent 1568602fec
commit b39efcb82e
7 changed files with 151 additions and 49 deletions

2
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/containerd/log v0.1.0
github.com/containerd/nri v0.11.0
github.com/containerd/otelttrpc v0.1.0
github.com/containerd/platforms v1.0.0-rc.2
github.com/containerd/platforms v1.0.0-rc.3
github.com/containerd/plugin v1.0.0
github.com/containerd/ttrpc v1.2.8
github.com/containerd/typeurl/v2 v2.2.3

4
go.sum
View File

@@ -65,8 +65,8 @@ github.com/containerd/nri v0.11.0 h1:26mcQwNG58AZn0YkOrlJQ0yxQVmyZooflnVWJTqQrqQ
github.com/containerd/nri v0.11.0/go.mod h1:bjGTLdUA58WgghKHg8azFMGXr05n1wDHrt3NSVBHiGI=
github.com/containerd/otelttrpc v0.1.0 h1:UOX68eVTE8H/T45JveIg+I22Ev2aFj4qPITCmXsskjw=
github.com/containerd/otelttrpc v0.1.0/go.mod h1:XhoA2VvaGPW1clB2ULwrBZfXVuEWuyOd2NUD1IM0yTg=
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/platforms v1.0.0-rc.3 h1:YdvwaHtrN6wHcGJ2mYRYP3Nso8OcysuqFe9Hxm1X/tI=
github.com/containerd/platforms v1.0.0-rc.3/go.mod h1:gw0R+alP3nFQPh1L4K9bv13fRWeeyokLGLu2fKuqI10=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y=

View File

@@ -1,32 +1,25 @@
version: "2"
linters:
enable:
- copyloopvar
- gofmt
- goimports
- dupword
- gosec
- ineffassign
- misspell
- nolintlint
- revive
- staticcheck
- tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
- unconvert
- unused
- govet
- dupword # Checks for duplicate words in the source code
disable:
- errcheck
run:
timeout: 5m
issues:
exclude-dirs:
- api
- cluster
- design
- docs
- docs/man
- releases
- reports
- test # e2e scripts
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax

View File

@@ -213,9 +213,20 @@ func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool
return true
}
if p1m || p2m {
if p1m && p2m {
// Prefer one with most matching features
if len(p1.OSFeatures) != len(p2.OSFeatures) {
return len(p1.OSFeatures) > len(p2.OSFeatures)
}
}
return false
}
}
if len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0 {
p1.OSFeatures = nil
p2.OSFeatures = nil
return c.Less(p1, p2)
}
return false
}
@@ -242,9 +253,20 @@ func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
p2m = true
}
if p1m && p2m {
return false
if len(p1.OSFeatures) != len(p2.OSFeatures) {
return len(p1.OSFeatures) > len(p2.OSFeatures)
}
break
}
}
// If neither match and has features, strip features and compare
if !p1m && !p2m && (len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0) {
p1.OSFeatures = nil
p2.OSFeatures = nil
return c.Less(p1, p2)
}
// If one matches, and the other does, sort match first
return p1m && !p2m
}

View File

@@ -27,7 +27,8 @@ func getCPUVariant() (string, error) {
var variant string
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
switch runtime.GOOS {
case "windows", "darwin":
// Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
// runtime.GOARCH to determine the variants
switch runtime.GOARCH {
@@ -38,7 +39,7 @@ func getCPUVariant() (string, error) {
default:
variant = "unknown"
}
} else if runtime.GOOS == "freebsd" {
case "freebsd":
// FreeBSD supports ARMv6 and ARMv7 as well as ARMv4 and ARMv5 (though deprecated)
// detecting those variants is currently unimplemented
switch runtime.GOARCH {
@@ -47,7 +48,7 @@ func getCPUVariant() (string, error) {
default:
variant = "unknown"
}
} else {
default:
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
}

View File

@@ -111,9 +111,11 @@ package platforms
import (
"fmt"
"net/url"
"path"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
@@ -121,12 +123,10 @@ import (
)
var (
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`)
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
osRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.%-]*)((?:\+[A-Za-z0-9_.%-]+)*)\))?$`)
)
const osAndVersionFormat = "%s(%s)"
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
type Platform = specs.Platform
@@ -143,6 +143,10 @@ type Matcher interface {
// functionality.
//
// Applications should opt to use `Match` over directly parsing specifiers.
//
// For OSFeatures, this matcher will match if the platform to match has
// OSFeatures which are a subset of the OSFeatures of the platform
// provided to NewMatcher.
func NewMatcher(platform specs.Platform) Matcher {
m := &matcher{
Platform: Normalize(platform),
@@ -178,10 +182,39 @@ type matcher struct {
func (m *matcher) Match(platform specs.Platform) bool {
normalized := Normalize(platform)
return m.OS == normalized.OS &&
if m.OS == normalized.OS &&
m.Architecture == normalized.Architecture &&
m.Variant == normalized.Variant &&
m.matchOSVersion(platform)
m.matchOSVersion(platform) {
if len(normalized.OSFeatures) == 0 {
return true
}
if len(m.OSFeatures) >= len(normalized.OSFeatures) {
// Ensure that normalized.OSFeatures is a subset of
// m.OSFeatures
j := 0
for _, feature := range normalized.OSFeatures {
found := false
for ; j < len(m.OSFeatures); j++ {
if feature == m.OSFeatures[j] {
found = true
j++
break
}
// Since both lists are ordered, if the feature is less
// than what is seen, it is not in the list
if feature < m.OSFeatures[j] {
return false
}
}
if !found {
return false
}
}
return true
}
}
return false
}
func (m *matcher) matchOSVersion(platform specs.Platform) bool {
@@ -210,11 +243,14 @@ func ParseAll(specifiers []string) ([]specs.Platform, error) {
// Parse parses the platform specifier syntax into a platform declaration.
//
// Platform specifiers are in the format `<os>[(<OSVersion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]`.
// Platform specifiers are in the format `<os>[(<os options>)]|<arch>|<os>[(<os options>)]/<arch>[/<variant>]`.
// The minimum required information for a platform specifier is the operating
// system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)`
// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value,
// and an empty string otherwise.
// system or architecture. The "os options" may be OSVersion which can be part of the OS
// like `windows(10.0.17763)`. When an OSVersion is specified, then specs.Platform.OSVersion is
// populated with that value, and an empty string otherwise. The "os options" may also include an
// array of OSFeatures, each feature prefixed with '+', without any other separator, and provided
// after the OSVersion when the OSVersion is specified. An "os options" with version and features
// is like `windows(10.0.17763+win32k)`.
// If there is only a single string (no slashes), the
// value will be matched against the known set of operating systems, then fall
// back to the known set of architectures. The missing component will be
@@ -231,14 +267,28 @@ func Parse(specifier string) (specs.Platform, error) {
var p specs.Platform
for i, part := range parts {
if i == 0 {
// First element is <os>[(<OSVersion>)]
osVer := osAndVersionRe.FindStringSubmatch(part)
if osVer == nil {
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument)
// First element is <os>[(<OSVersion>[+<OSFeature>]*)]
osOptions := osRe.FindStringSubmatch(part)
if osOptions == nil {
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osRe.String(), errInvalidArgument)
}
p.OS = normalizeOS(osVer[1])
p.OSVersion = osVer[2]
p.OS = normalizeOS(osOptions[1])
osVersion, err := decodeOSOption(osOptions[2])
if err != nil {
return specs.Platform{}, fmt.Errorf("%q has an invalid OS version %q: %w", specifier, osOptions[2], err)
}
p.OSVersion = osVersion
if osOptions[3] != "" {
rawFeatures := strings.Split(osOptions[3][1:], "+")
p.OSFeatures = make([]string, len(rawFeatures))
for i, f := range rawFeatures {
p.OSFeatures[i], err = decodeOSOption(f)
if err != nil {
return specs.Platform{}, fmt.Errorf("%q has an invalid OS feature %q: %w", specifier, f, err)
}
}
}
} else {
if !specifierRe.MatchString(part) {
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
@@ -322,13 +372,44 @@ func FormatAll(platform specs.Platform) string {
return "unknown"
}
if platform.OSVersion != "" {
OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion)
osOptions := encodeOSOption(platform.OSVersion)
features := platform.OSFeatures
if !slices.IsSorted(features) {
features = slices.Clone(features)
slices.Sort(features)
}
for _, f := range features {
osOptions += "+" + encodeOSOption(f)
}
if osOptions != "" {
OSAndVersion := fmt.Sprintf("%s(%s)", platform.OS, osOptions)
return path.Join(OSAndVersion, platform.Architecture, platform.Variant)
}
return path.Join(platform.OS, platform.Architecture, platform.Variant)
}
// osOptionReplacer encodes characters in OS option values (version and
// features) that are ambiguous with the format syntax. The percent sign
// must be replaced first to avoid double-encoding.
var osOptionReplacer = strings.NewReplacer(
"%", "%25",
"+", "%2B",
"(", "%28",
")", "%29",
"/", "%2F",
)
func encodeOSOption(v string) string {
return osOptionReplacer.Replace(v)
}
func decodeOSOption(v string) (string, error) {
if strings.Contains(v, "%") {
return url.PathUnescape(v)
}
return v, nil
}
// Normalize validates and translate the platform to the canonical value.
//
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
@@ -336,6 +417,11 @@ func FormatAll(platform specs.Platform) string {
func Normalize(platform specs.Platform) specs.Platform {
platform.OS = normalizeOS(platform.OS)
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
if len(platform.OSFeatures) > 0 {
platform.OSFeatures = slices.Clone(platform.OSFeatures)
slices.Sort(platform.OSFeatures)
platform.OSFeatures = slices.Compact(platform.OSFeatures)
}
return platform
}

4
vendor/modules.txt vendored
View File

@@ -197,8 +197,8 @@ github.com/containerd/nri/types/v1
## explicit; go 1.21
github.com/containerd/otelttrpc
github.com/containerd/otelttrpc/internal
# github.com/containerd/platforms v1.0.0-rc.2
## explicit; go 1.20
# github.com/containerd/platforms v1.0.0-rc.3
## explicit; go 1.21
github.com/containerd/platforms
# github.com/containerd/plugin v1.0.0
## explicit; go 1.20