Merge pull request #43321 from corhere/43284-report-displayversion

pkg/parsers: support Windows 11: report DisplayVersion; drop ProductName
This commit is contained in:
Sebastiaan van Stijn 2022-03-16 12:41:50 +01:00 committed by GitHub
commit 1133d55770
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 35 deletions

View file

@ -1,53 +1,55 @@
package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem"
import (
"fmt"
"errors"
"github.com/Microsoft/hcsshim/osversion"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
// VER_NT_WORKSTATION, see https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
const verNTWorkstation = 0x00000001 // VER_NT_WORKSTATION
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) {
os, err := withCurrentVersionRegistryKey(func(key registry.Key) (os string, err error) {
if os, _, err = key.GetStringValue("ProductName"); err != nil {
return "", err
}
releaseId, _, err := key.GetStringValue("ReleaseId")
if err != nil {
return
}
os = fmt.Sprintf("%s Version %s", os, releaseId)
buildNumber, _, err := key.GetStringValue("CurrentBuildNumber")
if err != nil {
return
}
ubr, _, err := key.GetIntegerValue("UBR")
if err != nil {
return
}
os = fmt.Sprintf("%s (OS Build %s.%d)", os, buildNumber, ubr)
return
})
if os == "" {
// Default return value
os = "Unknown Operating System"
osversion := windows.RtlGetVersion() // Always succeeds.
rel := windowsOSRelease{
IsServer: osversion.ProductType != verNTWorkstation,
Build: osversion.BuildNumber,
}
return os, err
// Make a best-effort attempt to retrieve the display version and
// Update Build Revision by querying undocumented registry values.
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err == nil {
defer key.Close()
if ver, err := getFirstStringValue(key,
"DisplayVersion", /* Windows 20H2 and above */
"ReleaseId", /* Windows 2009 and below */
); err == nil {
rel.DisplayVersion = ver
}
if ubr, _, err := key.GetIntegerValue("UBR"); err == nil {
rel.UBR = ubr
}
}
return rel.String(), nil
}
func withCurrentVersionRegistryKey(f func(registry.Key) (string, error)) (string, error) {
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return "", err
func getFirstStringValue(key registry.Key, names ...string) (string, error) {
for _, n := range names {
val, _, err := key.GetStringValue(n)
if err != nil {
if !errors.Is(err, registry.ErrNotExist) {
return "", err
}
continue
}
return val, nil
}
defer key.Close()
return f(key)
return "", registry.ErrNotExist
}
// GetOperatingSystemVersion gets the version of the current operating system, as a string.

View file

@ -0,0 +1,33 @@
package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem"
import (
"fmt"
"strings"
)
type windowsOSRelease struct {
IsServer bool
DisplayVersion string
Build uint32
UBR uint64
}
// String formats the OS release data similar to what is displayed by
// winver.exe.
func (r *windowsOSRelease) String() string {
var b strings.Builder
b.WriteString("Microsoft Windows")
if r.IsServer {
b.WriteString(" Server")
}
if r.DisplayVersion != "" {
b.WriteString(" Version ")
b.WriteString(r.DisplayVersion)
}
_, _ = fmt.Fprintf(&b, " (OS Build %d", r.Build)
if r.UBR > 0 {
_, _ = fmt.Fprintf(&b, ".%d", r.UBR)
}
b.WriteByte(')')
return b.String()
}

View file

@ -0,0 +1,89 @@
package operatingsystem
import (
"testing"
)
func Test_windowsOSRelease_String(t *testing.T) {
tests := []struct {
name string
r windowsOSRelease
want string
}{
{
name: "Flavor=client/DisplayVersion=yes/UBR=yes",
r: windowsOSRelease{
DisplayVersion: "1809",
Build: 17763,
UBR: 2628,
},
want: "Microsoft Windows Version 1809 (OS Build 17763.2628)",
},
{
name: "Flavor=client/DisplayVersion=yes/UBR=no",
r: windowsOSRelease{
DisplayVersion: "1809",
Build: 17763,
},
want: "Microsoft Windows Version 1809 (OS Build 17763)",
},
{
name: "Flavor=client/DisplayVersion=no/UBR=yes",
r: windowsOSRelease{
Build: 17763,
UBR: 1879,
},
want: "Microsoft Windows (OS Build 17763.1879)",
},
{
name: "Flavor=client/DisplayVersion=no/UBR=no",
r: windowsOSRelease{
Build: 10240,
},
want: "Microsoft Windows (OS Build 10240)",
},
{
name: "Flavor=server/DisplayVersion=yes/UBR=yes",
r: windowsOSRelease{
IsServer: true,
DisplayVersion: "21H2",
Build: 20348,
UBR: 169,
},
want: "Microsoft Windows Server Version 21H2 (OS Build 20348.169)",
},
{
name: "Flavor=server/DisplayVersion=yes/UBR=no",
r: windowsOSRelease{
IsServer: true,
DisplayVersion: "20H2",
Build: 19042,
},
want: "Microsoft Windows Server Version 20H2 (OS Build 19042)",
},
{
name: "Flavor=server/DisplayVersion=no/UBR=yes",
r: windowsOSRelease{
IsServer: true,
Build: 17763,
UBR: 107,
},
want: "Microsoft Windows Server (OS Build 17763.107)",
},
{
name: "Flavor=server/DisplayVersion=no/UBR=no",
r: windowsOSRelease{
IsServer: true,
Build: 17763,
},
want: "Microsoft Windows Server (OS Build 17763)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.r.String(); got != tt.want {
t.Errorf("windowsOSRelease.String() = %v, want %v", got, tt.want)
}
})
}
}