Merge pull request #43321 from corhere/43284-report-displayversion
pkg/parsers: support Windows 11: report DisplayVersion; drop ProductName
This commit is contained in:
commit
1133d55770
3 changed files with 159 additions and 35 deletions
|
@ -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.
|
||||
|
|
33
pkg/parsers/operatingsystem/windows_os_string.go
Normal file
33
pkg/parsers/operatingsystem/windows_os_string.go
Normal 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()
|
||||
}
|
89
pkg/parsers/operatingsystem/windows_os_string_test.go
Normal file
89
pkg/parsers/operatingsystem/windows_os_string_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue