mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +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",
|
"//vendor/github.com/google/cadvisor/info/v2:go_default_library",
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:windows_amd64": [
|
"@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/golang/glog:go_default_library",
|
||||||
"//vendor/github.com/lxn/win:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
],
|
],
|
||||||
"//conditions:default": [],
|
"//conditions:default": [],
|
||||||
|
@ -25,14 +25,20 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||||
"github.com/lxn/win"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procGetPhysicallyInstalledSystemMemory = modkernel32.NewProc("GetPhysicallyInstalledSystemMemory")
|
||||||
|
)
|
||||||
|
|
||||||
// NewPerfCounterClient creates a client using perf counters
|
// NewPerfCounterClient creates a client using perf counters
|
||||||
func NewPerfCounterClient() (Client, error) {
|
func NewPerfCounterClient() (Client, error) {
|
||||||
return newClient(&perfCounterNodeStatsClient{})
|
return newClient(&perfCounterNodeStatsClient{})
|
||||||
@ -51,13 +57,18 @@ func (p *perfCounterNodeStatsClient) startMonitoring() error {
|
|||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
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{
|
p.nodeInfo = nodeInfo{
|
||||||
kernelVersion: kernelVersion,
|
kernelVersion: kernelVersion,
|
||||||
osImageVersion: osImageVersion,
|
osImageVersion: osImageVersion,
|
||||||
@ -158,9 +169,18 @@ func (p *perfCounterNodeStatsClient) convertCPUValue(cpuValue uint64) uint64 {
|
|||||||
func getPhysicallyInstalledSystemMemoryBytes() (uint64, error) {
|
func getPhysicallyInstalledSystemMemoryBytes() (uint64, error) {
|
||||||
var physicalMemoryKiloBytes uint64
|
var physicalMemoryKiloBytes uint64
|
||||||
|
|
||||||
if ok := win.GetPhysicallyInstalledSystemMemory(&physicalMemoryKiloBytes); !ok {
|
if ok := getPhysicallyInstalledSystemMemory(&physicalMemoryKiloBytes); !ok {
|
||||||
return 0, errors.New("unable to read physical memory")
|
return 0, errors.New("unable to read physical memory")
|
||||||
}
|
}
|
||||||
|
|
||||||
return physicalMemoryKiloBytes * 1024, nil // convert kilobytes to bytes
|
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"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/lxn/win"
|
"github.com/JeffAshton/win_pdh"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -37,31 +37,31 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type perfCounter struct {
|
type perfCounter struct {
|
||||||
queryHandle win.PDH_HQUERY
|
queryHandle win_pdh.PDH_HQUERY
|
||||||
counterHandle win.PDH_HCOUNTER
|
counterHandle win_pdh.PDH_HCOUNTER
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPerfCounter(counter string) (*perfCounter, error) {
|
func newPerfCounter(counter string) (*perfCounter, error) {
|
||||||
var queryHandle win.PDH_HQUERY
|
var queryHandle win_pdh.PDH_HQUERY
|
||||||
var counterHandle win.PDH_HCOUNTER
|
var counterHandle win_pdh.PDH_HCOUNTER
|
||||||
|
|
||||||
ret := win.PdhOpenQuery(0, 0, &queryHandle)
|
ret := win_pdh.PdhOpenQuery(0, 0, &queryHandle)
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return nil, errors.New("unable to open query through DLL call")
|
return nil, errors.New("unable to open query through DLL call")
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = win.PdhValidatePath(counter)
|
ret = win_pdh.PdhValidatePath(counter)
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return nil, fmt.Errorf("unable to valid path to counter. Error code is %x", ret)
|
return nil, fmt.Errorf("unable to valid path to counter. Error code is %x", ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = win.PdhAddEnglishCounter(queryHandle, counter, 0, &counterHandle)
|
ret = win_pdh.PdhAddEnglishCounter(queryHandle, counter, 0, &counterHandle)
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return nil, fmt.Errorf("unable to add process counter. Error code is %x", ret)
|
return nil, fmt.Errorf("unable to add process counter. Error code is %x", ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = win.PdhCollectQueryData(queryHandle)
|
ret = win_pdh.PdhCollectQueryData(queryHandle)
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
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) {
|
func (p *perfCounter) getData() (uint64, error) {
|
||||||
ret := win.PdhCollectQueryData(p.queryHandle)
|
ret := win_pdh.PdhCollectQueryData(p.queryHandle)
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufSize, bufCount uint32
|
var bufSize, bufCount uint32
|
||||||
var size = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
var size = uint32(unsafe.Sizeof(win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||||
var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
var emptyBuf [1]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||||
var data uint64
|
var data uint64
|
||||||
|
|
||||||
ret = win.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
|
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
|
||||||
if ret != win.PDH_MORE_DATA {
|
if ret != win_pdh.PDH_MORE_DATA {
|
||||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
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)
|
filledBuf := make([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||||
ret = win.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
||||||
if ret != win.ERROR_SUCCESS {
|
if ret != win_pdh.ERROR_SUCCESS {
|
||||||
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
|
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
|
package winstats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||||
@ -135,11 +134,3 @@ func (c *statsClient) createRootContainerInfo() (*cadvisorapiv2.ContainerInfo, e
|
|||||||
|
|
||||||
return &rootInfo, nil
|
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"})
|
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 {
|
func getClient(t *testing.T) Client {
|
||||||
f := fakeWinNodeStatsClient{}
|
f := fakeWinNodeStatsClient{}
|
||||||
c, err := newClient(f)
|
c, err := newClient(f)
|
||||||
|
Loading…
Reference in New Issue
Block a user