...

Source file src/k8s.io/kubernetes/pkg/kubelet/util/util_windows.go

Documentation: k8s.io/kubernetes/pkg/kubelet/util

     1  //go:build windows
     2  // +build windows
     3  
     4  /*
     5  Copyright 2017 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package util
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"net"
    26  	"net/url"
    27  	"path/filepath"
    28  	"strings"
    29  	"syscall"
    30  	"time"
    31  
    32  	"github.com/Microsoft/go-winio"
    33  )
    34  
    35  const (
    36  	tcpProtocol   = "tcp"
    37  	npipeProtocol = "npipe"
    38  )
    39  
    40  // CreateListener creates a listener on the specified endpoint.
    41  func CreateListener(endpoint string) (net.Listener, error) {
    42  	protocol, addr, err := parseEndpoint(endpoint)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	switch protocol {
    48  	case tcpProtocol:
    49  		return net.Listen(tcpProtocol, addr)
    50  
    51  	case npipeProtocol:
    52  		return winio.ListenPipe(addr, nil)
    53  
    54  	default:
    55  		return nil, fmt.Errorf("only support tcp and npipe endpoint")
    56  	}
    57  }
    58  
    59  // GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
    60  func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
    61  	protocol, addr, err := parseEndpoint(endpoint)
    62  	if err != nil {
    63  		return "", nil, err
    64  	}
    65  
    66  	if protocol == tcpProtocol {
    67  		return addr, tcpDial, nil
    68  	}
    69  
    70  	if protocol == npipeProtocol {
    71  		return addr, npipeDial, nil
    72  	}
    73  
    74  	return "", nil, fmt.Errorf("only support tcp and npipe endpoint")
    75  }
    76  
    77  func tcpDial(ctx context.Context, addr string) (net.Conn, error) {
    78  	return (&net.Dialer{}).DialContext(ctx, tcpProtocol, addr)
    79  }
    80  
    81  func npipeDial(ctx context.Context, addr string) (net.Conn, error) {
    82  	return winio.DialPipeContext(ctx, addr)
    83  }
    84  
    85  func parseEndpoint(endpoint string) (string, string, error) {
    86  	// url.Parse doesn't recognize \, so replace with / first.
    87  	endpoint = strings.Replace(endpoint, "\\", "/", -1)
    88  	u, err := url.Parse(endpoint)
    89  	if err != nil {
    90  		return "", "", err
    91  	}
    92  
    93  	if u.Scheme == "tcp" {
    94  		return "tcp", u.Host, nil
    95  	} else if u.Scheme == "npipe" {
    96  		if strings.HasPrefix(u.Path, "//./pipe") {
    97  			return "npipe", u.Path, nil
    98  		}
    99  
   100  		// fallback host if not provided.
   101  		host := u.Host
   102  		if host == "" {
   103  			host = "."
   104  		}
   105  		return "npipe", fmt.Sprintf("//%s%s", host, u.Path), nil
   106  	} else if u.Scheme == "" {
   107  		return "", "", fmt.Errorf("Using %q as endpoint is deprecated, please consider using full url format", endpoint)
   108  	} else {
   109  		return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
   110  	}
   111  }
   112  
   113  // LocalEndpoint returns the full path to a named pipe at the given endpoint - unlike on unix, we can't use sockets.
   114  func LocalEndpoint(path, file string) (string, error) {
   115  	// extract the podresources config name from the path. We only need this on windows because the preferred layout of pipes,
   116  	// this is why we have the extra logic in here instead of changing the function signature. Join the file to make sure the
   117  	// last path component is a file, so the operation chain works..
   118  	podResourcesDir := filepath.Base(filepath.Dir(filepath.Join(path, file)))
   119  	if podResourcesDir == "" {
   120  		// should not happen because the user can configure a root directory, and we expected a subdirectory inside
   121  		// the user supplied root directory named like "pod-resources" or so.
   122  		return "", fmt.Errorf("cannot infer the podresources directory from path %q", path)
   123  	}
   124  	// windows pipes are expected to use forward slashes: https://learn.microsoft.com/windows/win32/ipc/pipe-names
   125  	// so using `url` like we do on unix gives us unclear benefits - see https://github.com/kubernetes/kubernetes/issues/78628
   126  	// So we just construct the path from scratch.
   127  	// Format: \\ServerName\pipe\PipeName
   128  	// Where where ServerName is either the name of a remote computer or a period, to specify the local computer.
   129  	// We only consider PipeName as regular windows path, while the pipe path components are fixed, hence we use constants.
   130  	serverPart := `\\.`
   131  	pipePart := "pipe"
   132  	pipeName := "kubelet-" + podResourcesDir
   133  	return npipeProtocol + "://" + filepath.Join(serverPart, pipePart, pipeName), nil
   134  }
   135  
   136  var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64")
   137  
   138  // GetBootTime returns the time at which the machine was started, truncated to the nearest second
   139  func GetBootTime() (time.Time, error) {
   140  	currentTime := time.Now()
   141  	output, _, err := tickCount.Call()
   142  	if errno, ok := err.(syscall.Errno); !ok || errno != 0 {
   143  		return time.Time{}, err
   144  	}
   145  	return currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second), nil
   146  }
   147  
   148  // NormalizePath converts FS paths returned by certain go frameworks (like fsnotify)
   149  // to native Windows paths that can be passed to Windows specific code
   150  func NormalizePath(path string) string {
   151  	path = strings.ReplaceAll(path, "/", "\\")
   152  	if strings.HasPrefix(path, "\\") {
   153  		path = "c:" + path
   154  	}
   155  	return path
   156  }
   157  

View as plain text