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:
Jiangtian Li 2017-11-10 14:50:44 -08:00
parent fe399bda1a
commit b91e030fcf
5 changed files with 48 additions and 44 deletions

View File

@ -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": [],

View File

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

View File

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

View File

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

View File

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