Skip to content

Commit

Permalink
Merge pull request #3158 from TinaMor/tinamor/dev
Browse files Browse the repository at this point in the history
Fix nerdctl info on Windows
  • Loading branch information
AkihiroSuda authored Jul 10, 2024
2 parents cf881e2 + c3ea8ff commit 77e6f18
Show file tree
Hide file tree
Showing 5 changed files with 533 additions and 31 deletions.
62 changes: 40 additions & 22 deletions pkg/cmd/system/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ import (
"text/template"

"github.com/containerd/containerd"
"github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/log"
"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/docker/go-units"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/formatter"
"github.com/containerd/nerdctl/v2/pkg/infoutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
"github.com/containerd/nerdctl/v2/pkg/strutil"
"github.com/docker/go-units"
)

func Info(ctx context.Context, client *containerd.Client, options types.SystemInfoOptions) error {
Expand Down Expand Up @@ -153,13 +153,45 @@ func prettyPrintInfoDockerCompat(stdout io.Writer, stderr io.Writer, info *docke
// Storage Driver is not really Server concept for nerdctl, but mimics `docker info` output
fmt.Fprintf(w, " Storage Driver: %s\n", info.Driver)
fmt.Fprintf(w, " Logging Driver: %s\n", info.LoggingDriver)
fmt.Fprintf(w, " Cgroup Driver: %s\n", info.CgroupDriver)
fmt.Fprintf(w, " Cgroup Version: %s\n", info.CgroupVersion)
printF(w, " Cgroup Driver: ", info.CgroupDriver)
printF(w, " Cgroup Version: ", info.CgroupVersion)
fmt.Fprintf(w, " Plugins:\n")
fmt.Fprintf(w, " Log: %s\n", strings.Join(info.Plugins.Log, " "))
fmt.Fprintf(w, " Log: %s\n", strings.Join(info.Plugins.Log, " "))
fmt.Fprintf(w, " Storage: %s\n", strings.Join(info.Plugins.Storage, " "))

// print Security options
printSecurityOptions(w, info.SecurityOptions)

fmt.Fprintf(w, " Kernel Version: %s\n", info.KernelVersion)
fmt.Fprintf(w, " Operating System: %s\n", info.OperatingSystem)
fmt.Fprintf(w, " OSType: %s\n", info.OSType)
fmt.Fprintf(w, " Architecture: %s\n", info.Architecture)
fmt.Fprintf(w, " CPUs: %d\n", info.NCPU)
fmt.Fprintf(w, " Total Memory: %s\n", units.BytesSize(float64(info.MemTotal)))
fmt.Fprintf(w, " Name: %s\n", info.Name)
fmt.Fprintf(w, " ID: %s\n", info.ID)

fmt.Fprintln(w)
if len(info.Warnings) > 0 {
fmt.Fprintln(stderr, strings.Join(info.Warnings, "\n"))
}
return nil
}

func printF(w io.Writer, label string, dockerCompatInfo string) {
if dockerCompatInfo == "" {
return
}
fmt.Fprintf(w, " %s: %s\n", label, dockerCompatInfo)
}

func printSecurityOptions(w io.Writer, securityOptions []string) {
if len(securityOptions) == 0 {
return
}

fmt.Fprintf(w, " Security Options:\n")
for _, s := range info.SecurityOptions {
for _, s := range securityOptions {
m, err := strutil.ParseCSVMap(s)
if err != nil {
log.L.WithError(err).Warnf("unparsable security option %q", s)
Expand All @@ -175,21 +207,7 @@ func prettyPrintInfoDockerCompat(stdout io.Writer, stderr io.Writer, info *docke
if k == "name" {
continue
}
fmt.Fprintf(w, " %s: %s\n", cases.Title(language.English).String(k), v)
fmt.Fprintf(w, " %s:\t%s\n", cases.Title(language.English).String(k), v)
}
}
fmt.Fprintf(w, " Kernel Version: %s\n", info.KernelVersion)
fmt.Fprintf(w, " Operating System: %s\n", info.OperatingSystem)
fmt.Fprintf(w, " OSType: %s\n", info.OSType)
fmt.Fprintf(w, " Architecture: %s\n", info.Architecture)
fmt.Fprintf(w, " CPUs: %d\n", info.NCPU)
fmt.Fprintf(w, " Total Memory: %s\n", units.BytesSize(float64(info.MemTotal)))
fmt.Fprintf(w, " Name: %s\n", info.Name)
fmt.Fprintf(w, " ID: %s\n", info.ID)

fmt.Fprintln(w)
if len(info.Warnings) > 0 {
fmt.Fprintln(stderr, strings.Join(info.Warnings, "\n"))
}
return nil
}
1 change: 0 additions & 1 deletion pkg/infoutil/infoutil_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"io"
"os"
"regexp"

"strings"

"golang.org/x/sys/unix"
Expand Down
193 changes: 185 additions & 8 deletions pkg/infoutil/infoutil_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,210 @@
package infoutil

import (
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"fmt"
"runtime"
"strings"

"github.com/containerd/log"
"github.com/docker/docker/pkg/meminfo"
"github.com/docker/docker/pkg/sysinfo"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"

"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
)

// UnameR returns `uname -r`
const UnameO = "Microsoft Windows"

// MsiNTProductType is the product type of the operating system.
// https://learn.microsoft.com/en-us/windows/win32/msi/msintproducttype
// Ref: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
const (
verNTServer = 0x0000003
)

type windowsInfoUtil interface {
RtlGetVersion() *windows.OsVersionInfoEx
GetRegistryStringValue(key registry.Key, path string, name string) (string, error)
GetRegistryIntValue(key registry.Key, path string, name string) (int, error)
}

type winInfoUtil struct{}

// RtlGetVersion implements the RtlGetVersion method using the actual windows package
func (sw *winInfoUtil) RtlGetVersion() *windows.OsVersionInfoEx {
return windows.RtlGetVersion()
}

// UnameR returns the Kernel version
func UnameR() string {
return ""
util := &winInfoUtil{}
version, err := getKernelVersion(util)
if err != nil {
log.L.Error(err.Error())
}

return version
}

// UnameM returns `uname -m`
// UnameM returns the architecture of the system
func UnameM() string {
return ""
arch := runtime.GOARCH

if strings.ToLower(arch) == "amd64" {
return "x86_64"
}

// "386": 32-bit Intel/AMD processors (x86 architecture)
if strings.ToLower(arch) == "386" {
return "x86"
}

// arm, s390x, and so on
return arch
}

// DistroName returns version information about the currently running operating system
func DistroName() string {
return ""
util := &winInfoUtil{}
version, err := distroName(util)
if err != nil {
log.L.Error(err.Error())
}

return version
}

func distroName(sw windowsInfoUtil) (string, error) {
// Get the OS version information from the Windows registry
regPath := `SOFTWARE\Microsoft\Windows NT\CurrentVersion`

// Eg. 22631 (REG_SZ)
currBuildNo, err := sw.GetRegistryStringValue(registry.LOCAL_MACHINE, regPath, "CurrentBuildNumber")
if err != nil {
return "", fmt.Errorf("failed to get os version (build number) %v", err)
}

// Eg. 23H2 (REG_SZ)
displayVersion, err := sw.GetRegistryStringValue(registry.LOCAL_MACHINE, regPath, "DisplayVersion")
if err != nil {
return "", fmt.Errorf("failed to get os version (display version) %v", err)
}

// UBR: Update Build Revision. Eg. 3737 (REG_DWORD 32-bit Value)
ubr, err := sw.GetRegistryIntValue(registry.LOCAL_MACHINE, regPath, "UBR")
if err != nil {
return "", fmt.Errorf("failed to get os version (ubr) %v", err)
}

productType := ""
if isWindowsServer(sw) {
productType = "Server"
}

// Concatenate the reg.key values to get the OS version information
// Example: "Microsoft Windows Version 23H2 (OS Build 22631.3737)"
versionString := fmt.Sprintf("%s %s Version %s (OS Build %s.%d)",
UnameO,
productType,
displayVersion,
currBuildNo,
ubr,
)

// Replace double spaces with single spaces
versionString = strings.ReplaceAll(versionString, " ", " ")

return versionString, nil
}

func getKernelVersion(sw windowsInfoUtil) (string, error) {
// Get BuildLabEx value from the Windows registry
// [buiild number].[revision number].[architecture].[branch].[date]-[time]
// Eg. "BuildLabEx: 10240.16412.amd64fre.th1.150729-1800"
buildLab, err := sw.GetRegistryStringValue(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, "BuildLabEx")
if err != nil {
return "", err
}

// Get Version: Contains the major and minor version numbers of the operating system.
// Eg. "10.0"
osvi := sw.RtlGetVersion()

// Concatenate the OS version and BuildLabEx values to get the Kernel version information
// Example: "10.0 22631 (10240.16412.amd64fre.th1.150729-1800)"
version := fmt.Sprintf("%d.%d %d (%s)", osvi.MajorVersion, osvi.MinorVersion, osvi.BuildNumber, buildLab)
return version, nil
}

// GetRegistryStringValue retrieves a string value from the Windows registry
func (sw *winInfoUtil) GetRegistryStringValue(key registry.Key, path string, name string) (string, error) {
k, err := registry.OpenKey(key, path, registry.QUERY_VALUE)
if err != nil {
return "", err
}
defer k.Close()

v, _, err := k.GetStringValue(name)
if err != nil {
return "", err
}
return v, nil
}

// GetRegistryIntValue retrieves an integer value from the Windows registry
func (sw *winInfoUtil) GetRegistryIntValue(key registry.Key, path string, name string) (int, error) {
k, err := registry.OpenKey(key, path, registry.QUERY_VALUE)
if err != nil {
return 0, err
}
defer k.Close()

v, _, err := k.GetIntegerValue(name)
if err != nil {
return 0, err
}
return int(v), nil
}

func isWindowsServer(sw windowsInfoUtil) bool {
osvi := sw.RtlGetVersion()
return osvi.ProductType == verNTServer
}

// Cgroups not supported on Windows
func CgroupsVersion() string {
return ""
}

func fulfillPlatformInfo(info *dockercompat.Info) {
// unimplemented
mobySysInfo := mobySysInfo(info)

// NOTE: cgroup fields are not available on Windows
// https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332

info.IPv4Forwarding = !mobySysInfo.IPv4ForwardingDisabled
if !info.IPv4Forwarding {
info.Warnings = append(info.Warnings, "WARNING: IPv4 forwarding is disabled")
}
info.BridgeNfIptables = !mobySysInfo.BridgeNFCallIPTablesDisabled
if !info.BridgeNfIptables {
info.Warnings = append(info.Warnings, "WARNING: bridge-nf-call-iptables is disabled")
}
info.BridgeNfIP6tables = !mobySysInfo.BridgeNFCallIP6TablesDisabled
if !info.BridgeNfIP6tables {
info.Warnings = append(info.Warnings, "WARNING: bridge-nf-call-ip6tables is disabled")
}
info.NCPU = sysinfo.NumCPU()
memLimit, err := meminfo.Read()
if err != nil {
info.Warnings = append(info.Warnings, fmt.Sprintf("failed to read mem info: %v", err))
} else {
info.MemTotal = memLimit.MemTotal
}
}

func mobySysInfo(info *dockercompat.Info) *sysinfo.SysInfo {
func mobySysInfo(_ *dockercompat.Info) *sysinfo.SysInfo {
var sysinfo sysinfo.SysInfo
return &sysinfo
}
Loading

0 comments on commit 77e6f18

Please sign in to comment.