From 6b4ffdb9f75fd2e385bae033cd60b6757f844ca2 Mon Sep 17 00:00:00 2001 From: Francesco Romani Date: Tue, 17 Jan 2023 15:43:14 +0100 Subject: [PATCH] node: re-implement Localendpoint on windows this will allows us to move forward with the podresources endpoint GA graduation. xref: https://github.com/kubernetes/kubernetes/issues/78628 Signed-off-by: Francesco Romani --- pkg/kubelet/server/server.go | 1 + pkg/kubelet/util/util_windows.go | 23 ++++++++++++-- pkg/kubelet/util/util_windows_test.go | 45 +++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go index 61a68be0b4c..beaaeabb4a8 100644 --- a/pkg/kubelet/server/server.go +++ b/pkg/kubelet/server/server.go @@ -230,6 +230,7 @@ func ListenAndServePodResources(endpoint string, providers podresources.PodResou os.Exit(1) } + klog.InfoS("Starting to serve the podresources API", "endpoint", endpoint) if err := server.Serve(l); err != nil { klog.ErrorS(err, "Failed to serve") os.Exit(1) diff --git a/pkg/kubelet/util/util_windows.go b/pkg/kubelet/util/util_windows.go index af51d45c605..33a279e9a1a 100644 --- a/pkg/kubelet/util/util_windows.go +++ b/pkg/kubelet/util/util_windows.go @@ -24,6 +24,7 @@ import ( "fmt" "net" "net/url" + "path/filepath" "strings" "syscall" "time" @@ -117,9 +118,27 @@ func parseEndpoint(endpoint string) (string, string, error) { } } -// LocalEndpoint empty implementation +// LocalEndpoint returns the full path to a named pipe at the given endpoint - unlike on unix, we can't use sockets. func LocalEndpoint(path, file string) (string, error) { - return "", fmt.Errorf("LocalEndpoints are unsupported in this build") + // extract the podresources config name from the path. We only need this on windows because the preferred layout of pipes, + // this is why we have the extra logic in here instead of changing the function signature. Join the file to make sure the + // last path component is a file, so the operation chain works.. + podResourcesDir := filepath.Base(filepath.Dir(filepath.Join(path, file))) + if podResourcesDir == "" { + // should not happen because the user can configure a root directory, and we expected a subdirectory inside + // the user supplied root directory named like "pod-resources" or so. + return "", fmt.Errorf("cannot infer the podresources directory from path %q", path) + } + // windows pipes are expected to use forward slashes: https://learn.microsoft.com/windows/win32/ipc/pipe-names + // so using `url` like we do on unix gives us unclear benefits - see https://github.com/kubernetes/kubernetes/issues/78628 + // So we just construct the path from scratch. + // Format: \\ServerName\pipe\PipeName + // Where where ServerName is either the name of a remote computer or a period, to specify the local computer. + // We only consider PipeName as regular windows path, while the pipe path components are fixed, hence we use constants. + serverPart := `\\.` + pipePart := "pipe" + pipeName := "kubelet-" + podResourcesDir + return npipeProtocol + "://" + filepath.Join(serverPart, pipePart, pipeName), nil } var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64") diff --git a/pkg/kubelet/util/util_windows_test.go b/pkg/kubelet/util/util_windows_test.go index 54d08f0bf96..d718f6add3d 100644 --- a/pkg/kubelet/util/util_windows_test.go +++ b/pkg/kubelet/util/util_windows_test.go @@ -277,3 +277,48 @@ func TestNormalizePath(t *testing.T) { assert.Equal(t, test.normalizedPath, NormalizePath(test.originalpath)) } } + +func TestLocalEndpoint(t *testing.T) { + tests := []struct { + path string + file string + expectError bool + expectedFullPath string + }{ + { + path: "/var/lib/kubelet/pod-resources", + file: "kube.sock", // this is not the default, but it's not relevant here + expectError: false, + expectedFullPath: `npipe://\\.\pipe\kubelet-pod-resources`, + }, + } + for _, test := range tests { + fullPath, err := LocalEndpoint(test.path, test.file) + if test.expectError { + assert.NotNil(t, err, "expected error") + continue + } + assert.Nil(t, err, "expected no error") + assert.Equal(t, test.expectedFullPath, fullPath) + } +} + +func TestLocalEndpointRoundTrip(t *testing.T) { + npipeDialPointer := reflect.ValueOf(npipeDial).Pointer() + expectedDialerName := runtime.FuncForPC(npipeDialPointer).Name() + expectedAddress := "//./pipe/kubelet-pod-resources" + + fullPath, err := LocalEndpoint(`pod-resources`, "kubelet") + require.NoErrorf(t, err, "Failed to create the local endpoint path") + + address, dialer, err := GetAddressAndDialer(fullPath) + require.NoErrorf(t, err, "Failed to parse the endpoint path and get back address and dialer (path=%q)", fullPath) + + dialerPointer := reflect.ValueOf(dialer).Pointer() + actualDialerName := runtime.FuncForPC(dialerPointer).Name() + + assert.Equalf(t, npipeDialPointer, dialerPointer, + "Expected dialer %s, but get %s", expectedDialerName, actualDialerName) + + assert.Equal(t, expectedAddress, address) +}