Skip to content

Commit

Permalink
Add grammar for platform string
Browse files Browse the repository at this point in the history
Platform string to be of the form <os>[(<osversion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]
OSVersion is optional only and currently used only by Windows OS.

Signed-off-by: Kirtana Ashok <[email protected]>
  • Loading branch information
kiashok committed Jan 31, 2024
1 parent db76a43 commit 81db5ed
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 27 deletions.
29 changes: 24 additions & 5 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package platforms

import (
"regexp"
"runtime"
"strings"
)
Expand Down Expand Up @@ -59,17 +60,35 @@ func isKnownArch(arch string) bool {
return false
}

func normalizeOS(os string) string {
if os == "" {
return runtime.GOOS
func getWindowsOsVersion(OSAndVersion string) string {
parts := regexp.MustCompile("[()]").Split(OSAndVersion, -1)
if len(parts) > 1 {
if parts[1] != "" {
return parts[1]
}
}
return ""
}

// The formart of OS part of the platform specifier is <OS>[(<OSVersion>)]
// OSVersion is optional only and is currently used only by windows OS.
// If OsVersion is not specified, empty string is returned.
func normalizeOS(OSAndVersion string) (string, string) {
if OSAndVersion == "" {
return runtime.GOOS, ""
}
os = strings.ToLower(os)
os := strings.Split(strings.ToLower(OSAndVersion), "(")[0]

switch os {
case "macos":
os = "darwin"
}
return os

// check for OS verion if OS version is windows. Otherwise, use the host OSVersion
if os == "windows" {
return "windows", getWindowsOsVersion(OSAndVersion)
}
return os, ""
}

// normalizeArch normalizes the architecture.
Expand Down
43 changes: 21 additions & 22 deletions platforms.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ import (
)

var (
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
specifierRe = regexp.MustCompile(`^[()A-Za-z0-9_.-]+$`)
OSAndVersionFormat = "%s(%s)"
)

// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
Expand Down Expand Up @@ -174,9 +175,13 @@ func ParseAll(specifiers []string) ([]specs.Platform, error) {

// Parse parses the platform specifier syntax into a platform declaration.
//
// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`.
// Platform specifiers are in the format `<os>[(<OSVersion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]`.
// The minimum required information for a platform specifier is the operating
// system or architecture. If there is only a single string (no slashes), the
// system or architecture. The OSVersion can be part of the OS like windows(10.0.17763)
// Currently, the OS version is only used by windows. Therefore, if the OS is windows
// and an os version is specified, then specs.Platform.OSVersion is populated. If not it
// is left empty.
// 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
// inferred based on the local environment.
Expand All @@ -197,23 +202,20 @@ func Parse(specifier string) (specs.Platform, error) {
var p specs.Platform
switch len(parts) {
case 1:
// in this case, we will test that the value might be an OS, then look
// it up. If it is not known, we'll treat it as an architecture. Since
// in this case, we will test that the value might be an OS (with or
// without the optional osversion specified) and look it up.
// If it is not known, we'll treat it as an architecture. Since
// we have very little information about the platform here, we are
// going to be a little more strict if we don't know about the argument
// value.
p.OS = normalizeOS(parts[0])
p.OS, p.OSVersion = normalizeOS(parts[0])
if isKnownOS(p.OS) {
// picks a default architecture
p.Architecture = runtime.GOARCH
if p.Architecture == "arm" && cpuVariant() != "v7" {
p.Variant = cpuVariant()
}

if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}

return p, nil
}

Expand All @@ -228,31 +230,23 @@ func Parse(specifier string) (specs.Platform, error) {

return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errInvalidArgument)
case 2:
// In this case, we treat as a regular os/arch pair. We don't care
// In this case, we treat as a regular os[(osversion)]/arch pair. We don't care
// about whether or not we know of the platform.
p.OS = normalizeOS(parts[0])
p.OS, p.OSVersion = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], "")
if p.Architecture == "arm" && p.Variant == "v7" {
p.Variant = ""
}

if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}

return p, nil
case 3:
// we have a fully specified variant, this is rare
p.OS = normalizeOS(parts[0])
p.OS, p.OSVersion = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
if p.Architecture == "arm64" && p.Variant == "" {
p.Variant = "v8"
}

if p.OS == "windows" {
p.OSVersion = GetWindowsOsVersion()
}

return p, nil
}

Expand All @@ -275,6 +269,11 @@ func Format(platform specs.Platform) string {
return "unknown"
}

if strings.ToLower(platform.OS) == "windows" && platform.OSVersion != "" {
windowsOsVersion := fmt.Sprintf(OSAndVersionFormat, platform.OS, platform.OSVersion)
return path.Join(windowsOsVersion, platform.Architecture, platform.Variant)
}

return path.Join(platform.OS, platform.Architecture, platform.Variant)
}

Expand All @@ -283,7 +282,7 @@ func Format(platform specs.Platform) string {
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
// "x86_64" is encountered, it becomes "amd64".
func Normalize(platform specs.Platform) specs.Platform {
platform.OS = normalizeOS(platform.OS)
platform.OS, platform.OSVersion = normalizeOS(platform.OS)
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)

return platform
Expand Down
30 changes: 30 additions & 0 deletions platforms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,36 @@ func TestParseSelector(t *testing.T) {
},
formatted: path.Join("darwin", defaultArch, defaultVariant),
},
{
input: "windows",
expected: specs.Platform{
OS: "windows",
OSVersion: "",
Architecture: defaultArch,
Variant: defaultVariant,
},
formatted: path.Join("windows", defaultArch, defaultVariant),
},
{
input: "windows()",
expected: specs.Platform{
OS: "windows",
OSVersion: "",
Architecture: defaultArch,
Variant: defaultVariant,
},
formatted: path.Join("windows", defaultArch, defaultVariant),
},
{
input: "windows(10.0.17763)",
expected: specs.Platform{
OS: "windows",
OSVersion: "10.0.17763",
Architecture: defaultArch,
Variant: defaultVariant,
},
formatted: path.Join("windows(10.0.17763)", defaultArch, defaultVariant),
},
} {
t.Run(testcase.input, func(t *testing.T) {
if testcase.skip {
Expand Down

0 comments on commit 81db5ed

Please sign in to comment.