Преглед изворни кода

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

pkg/parsers: support Windows 11: report DisplayVersion; drop ProductName
Sebastiaan van Stijn пре 3 година
родитељ
комит
1133d55770

+ 34 - 32
pkg/parsers/operatingsystem/operatingsystem_windows.go

@@ -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)
+	osversion := windows.RtlGetVersion() // Always succeeds.
+	rel := windowsOSRelease{
+		IsServer: osversion.ProductType != verNTWorkstation,
+		Build:    osversion.BuildNumber,
+	}
 
-		buildNumber, _, err := key.GetStringValue("CurrentBuildNumber")
-		if err != nil {
-			return
+	// 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
 		}
-		ubr, _, err := key.GetIntegerValue("UBR")
-		if err != nil {
-			return
+		if ubr, _, err := key.GetIntegerValue("UBR"); err == nil {
+			rel.UBR = ubr
 		}
-		os = fmt.Sprintf("%s (OS Build %s.%d)", os, buildNumber, ubr)
-
-		return
-	})
-
-	if os == "" {
-		// Default return value
-		os = "Unknown Operating System"
 	}
 
-	return os, err
+	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 - 0
pkg/parsers/operatingsystem/windows_os_string.go

@@ -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 - 0
pkg/parsers/operatingsystem/windows_os_string_test.go

@@ -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)
+			}
+		})
+	}
+}