mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
This PR fixes issue #55031 where kubelet.exe crashes on Windows Server Core. The root cause is that kubelet.exe depends on package lxn/win pdh and kernel32 wrapper for node metrics. However, opengl32.dll is not available in Server Core and lxn/win requires the presence of all win32 DLLs.
This PR uses a slim win32 package JeffAshton/win_pdh since most win32 APIs needed are PDH API. Also this PR makes own implementation of GetPhysicallyInstalledSystemMemory until golang Windows syscall has it or lxn/win fixes opengl32 issue. Also this PR modifies the way to get Windows version.
This commit is contained in:
parent
fe399bda1a
commit
b91e030fcf
@ -34,8 +34,8 @@ go_library(
|
||||
"//vendor/github.com/google/cadvisor/info/v2:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:windows_amd64": [
|
||||
"//vendor/github.com/JeffAshton/win_pdh:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/lxn/win:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
|
@ -25,14 +25,20 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/golang/glog"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
"github.com/lxn/win"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetPhysicallyInstalledSystemMemory = modkernel32.NewProc("GetPhysicallyInstalledSystemMemory")
|
||||
)
|
||||
|
||||
// NewPerfCounterClient creates a client using perf counters
|
||||
func NewPerfCounterClient() (Client, error) {
|
||||
return newClient(&perfCounterNodeStatsClient{})
|
||||
@ -51,13 +57,18 @@ func (p *perfCounterNodeStatsClient) startMonitoring() error {
|
||||
return err
|
||||
}
|
||||
|
||||
version, err := exec.Command("cmd", "/C", "ver").Output()
|
||||
iv, err := exec.Command("powershell", "-command", "(Get-CimInstance Win32_OperatingSystem).Caption").Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
osImageVersion := strings.TrimSpace(string(iv))
|
||||
|
||||
kv, err := exec.Command("powershell", "-command", "[System.Environment]::OSVersion.Version.ToString()").Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kernelVersion := strings.TrimSpace(string(kv))
|
||||
|
||||
osImageVersion := strings.TrimSpace(string(version))
|
||||
kernelVersion := extractVersionNumber(osImageVersion)
|
||||
p.nodeInfo = nodeInfo{
|
||||
kernelVersion: kernelVersion,
|
||||
osImageVersion: osImageVersion,
|
||||
@ -158,9 +169,18 @@ func (p *perfCounterNodeStatsClient) convertCPUValue(cpuValue uint64) uint64 {
|
||||
func getPhysicallyInstalledSystemMemoryBytes() (uint64, error) {
|
||||
var physicalMemoryKiloBytes uint64
|
||||
|
||||
if ok := win.GetPhysicallyInstalledSystemMemory(&physicalMemoryKiloBytes); !ok {
|
||||
if ok := getPhysicallyInstalledSystemMemory(&physicalMemoryKiloBytes); !ok {
|
||||
return 0, errors.New("unable to read physical memory")
|
||||
}
|
||||
|
||||
return physicalMemoryKiloBytes * 1024, nil // convert kilobytes to bytes
|
||||
}
|
||||
|
||||
func getPhysicallyInstalledSystemMemory(totalMemoryInKilobytes *uint64) bool {
|
||||
ret, _, _ := syscall.Syscall(procGetPhysicallyInstalledSystemMemory.Addr(), 1,
|
||||
uintptr(unsafe.Pointer(totalMemoryInKilobytes)),
|
||||
0,
|
||||
0)
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/lxn/win"
|
||||
"github.com/JeffAshton/win_pdh"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -37,31 +37,31 @@ const (
|
||||
)
|
||||
|
||||
type perfCounter struct {
|
||||
queryHandle win.PDH_HQUERY
|
||||
counterHandle win.PDH_HCOUNTER
|
||||
queryHandle win_pdh.PDH_HQUERY
|
||||
counterHandle win_pdh.PDH_HCOUNTER
|
||||
}
|
||||
|
||||
func newPerfCounter(counter string) (*perfCounter, error) {
|
||||
var queryHandle win.PDH_HQUERY
|
||||
var counterHandle win.PDH_HCOUNTER
|
||||
var queryHandle win_pdh.PDH_HQUERY
|
||||
var counterHandle win_pdh.PDH_HCOUNTER
|
||||
|
||||
ret := win.PdhOpenQuery(0, 0, &queryHandle)
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
ret := win_pdh.PdhOpenQuery(0, 0, &queryHandle)
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return nil, errors.New("unable to open query through DLL call")
|
||||
}
|
||||
|
||||
ret = win.PdhValidatePath(counter)
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
ret = win_pdh.PdhValidatePath(counter)
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return nil, fmt.Errorf("unable to valid path to counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
ret = win.PdhAddEnglishCounter(queryHandle, counter, 0, &counterHandle)
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
ret = win_pdh.PdhAddEnglishCounter(queryHandle, counter, 0, &counterHandle)
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return nil, fmt.Errorf("unable to add process counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
ret = win.PdhCollectQueryData(queryHandle)
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
ret = win_pdh.PdhCollectQueryData(queryHandle)
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
@ -72,24 +72,24 @@ func newPerfCounter(counter string) (*perfCounter, error) {
|
||||
}
|
||||
|
||||
func (p *perfCounter) getData() (uint64, error) {
|
||||
ret := win.PdhCollectQueryData(p.queryHandle)
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
ret := win_pdh.PdhCollectQueryData(p.queryHandle)
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
var bufSize, bufCount uint32
|
||||
var size = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||
var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||
var size = uint32(unsafe.Sizeof(win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||
var emptyBuf [1]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||
var data uint64
|
||||
|
||||
ret = win.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
|
||||
if ret != win.PDH_MORE_DATA {
|
||||
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
|
||||
if ret != win_pdh.PDH_MORE_DATA {
|
||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||
ret = win.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
||||
if ret != win.ERROR_SUCCESS {
|
||||
filledBuf := make([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
||||
if ret != win_pdh.ERROR_SUCCESS {
|
||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||
package winstats
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
@ -135,11 +134,3 @@ func (c *statsClient) createRootContainerInfo() (*cadvisorapiv2.ContainerInfo, e
|
||||
|
||||
return &rootInfo, nil
|
||||
}
|
||||
|
||||
// extractVersionNumber gets the version number from the full version string on Windows
|
||||
// e.g. extracts "10.0.14393" from "Microsoft Windows [Version 10.0.14393]"
|
||||
func extractVersionNumber(fullVersion string) string {
|
||||
re := regexp.MustCompile("[^0-9.]")
|
||||
version := re.ReplaceAllString(fullVersion, "")
|
||||
return version
|
||||
}
|
||||
|
@ -116,13 +116,6 @@ func TestWinVersionInfo(t *testing.T) {
|
||||
KernelVersion: "v42"})
|
||||
}
|
||||
|
||||
func TestExtractVersionNumber(t *testing.T) {
|
||||
fullVersion := "Microsoft Windows [Version 10.0.14393]"
|
||||
versionNumber := extractVersionNumber(fullVersion)
|
||||
expected := "10.0.14393"
|
||||
assert.Equal(t, expected, versionNumber)
|
||||
}
|
||||
|
||||
func getClient(t *testing.T) Client {
|
||||
f := fakeWinNodeStatsClient{}
|
||||
c, err := newClient(f)
|
||||
|
Loading…
Reference in New Issue
Block a user