k8s.io/kubernetes@v1.29.3/pkg/util/filesystem/util_windows.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  /*
     5  Copyright 2023 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 filesystem
    21  
    22  import (
    23  	"fmt"
    24  	"net"
    25  	"os"
    26  	"time"
    27  
    28  	"k8s.io/apimachinery/pkg/util/wait"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  const (
    33  	// Amount of time to wait between attempting to use a Unix domain socket.
    34  	// As detailed in https://github.com/kubernetes/kubernetes/issues/104584
    35  	// the first attempt will most likely fail, hence the need to retry
    36  	socketDialRetryPeriod = 1 * time.Second
    37  	// Overall timeout value to dial a Unix domain socket, including retries
    38  	socketDialTimeout = 4 * time.Second
    39  )
    40  
    41  // IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
    42  // Note that due to the retry logic inside, it could take up to 4 seconds
    43  // to determine whether or not the file path supplied is a Unix domain socket
    44  func IsUnixDomainSocket(filePath string) (bool, error) {
    45  	// Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
    46  	// we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file.
    47  
    48  	// Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
    49  	// for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket
    50  	// does NOT work in 1809 if the socket file is created within a bind mounted directory by a container
    51  	// and the FSCTL is issued in the host by the kubelet.
    52  
    53  	// If the file does not exist, it cannot be a Unix domain socket.
    54  	if _, err := os.Stat(filePath); os.IsNotExist(err) {
    55  		return false, fmt.Errorf("File %s not found. Err: %v", filePath, err)
    56  	}
    57  
    58  	klog.V(6).InfoS("Function IsUnixDomainSocket starts", "filePath", filePath)
    59  	// As detailed in https://github.com/kubernetes/kubernetes/issues/104584 we cannot rely
    60  	// on the Unix Domain socket working on the very first try, hence the potential need to
    61  	// dial multiple times
    62  	var lastSocketErr error
    63  	err := wait.PollImmediate(socketDialRetryPeriod, socketDialTimeout,
    64  		func() (bool, error) {
    65  			klog.V(6).InfoS("Dialing the socket", "filePath", filePath)
    66  			var c net.Conn
    67  			c, lastSocketErr = net.Dial("unix", filePath)
    68  			if lastSocketErr == nil {
    69  				c.Close()
    70  				klog.V(6).InfoS("Socket dialed successfully", "filePath", filePath)
    71  				return true, nil
    72  			}
    73  			klog.V(6).InfoS("Failed the current attempt to dial the socket, so pausing before retry",
    74  				"filePath", filePath, "err", lastSocketErr, "socketDialRetryPeriod",
    75  				socketDialRetryPeriod)
    76  			return false, nil
    77  		})
    78  
    79  	// PollImmediate will return "timed out waiting for the condition" if the function it
    80  	// invokes never returns true
    81  	if err != nil {
    82  		klog.V(2).InfoS("Failed all attempts to dial the socket so marking it as a non-Unix Domain socket. Last socket error along with the error from PollImmediate follow",
    83  			"filePath", filePath, "lastSocketErr", lastSocketErr, "err", err)
    84  		return false, nil
    85  	}
    86  	return true, nil
    87  }