mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 06:54:01 +00:00
Copy util directory for use in cri/remote package
Signed-off-by: Davanum Srinivas <davanum@gmail.com>
This commit is contained in:
parent
0608e8be25
commit
e049e594cd
@ -24,7 +24,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
kubeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
kubeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||||
apitest "k8s.io/cri-api/pkg/apis/testing"
|
apitest "k8s.io/cri-api/pkg/apis/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util"
|
"k8s.io/kubernetes/pkg/kubelet/cri/remote/util"
|
||||||
utilexec "k8s.io/utils/exec"
|
utilexec "k8s.io/utils/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
internalapi "k8s.io/cri-api/pkg/apis"
|
internalapi "k8s.io/cri-api/pkg/apis"
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util"
|
"k8s.io/kubernetes/pkg/kubelet/cri/remote/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RemoteImageService is a gRPC implementation of internalapi.ImageManagerService.
|
// RemoteImageService is a gRPC implementation of internalapi.ImageManagerService.
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
"k8s.io/component-base/logs/logreduction"
|
"k8s.io/component-base/logs/logreduction"
|
||||||
internalapi "k8s.io/cri-api/pkg/apis"
|
internalapi "k8s.io/cri-api/pkg/apis"
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util"
|
"k8s.io/kubernetes/pkg/kubelet/cri/remote/util"
|
||||||
utilexec "k8s.io/utils/exec"
|
utilexec "k8s.io/utils/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
145
pkg/kubelet/cri/remote/util/util_unix.go
Normal file
145
pkg/kubelet/cri/remote/util/util_unix.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// +build freebsd linux darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"k8s.io/klog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// unixProtocol is the network protocol of unix socket.
|
||||||
|
unixProtocol = "unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateListener creates a listener on the specified endpoint.
|
||||||
|
func CreateListener(endpoint string) (net.Listener, error) {
|
||||||
|
protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if protocol != unixProtocol {
|
||||||
|
return nil, fmt.Errorf("only support unix socket endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink to cleanup the previous socket file.
|
||||||
|
err = unix.Unlink(addr)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("failed to unlink socket file %q: %v", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(addr), 0750); err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating socket directory %q: %v", filepath.Dir(addr), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the socket on a tempfile and move it to the destination socket to handle improprer cleanup
|
||||||
|
file, err := ioutil.TempFile(filepath.Dir(addr), "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create temporary file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(file.Name()); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to remove temporary file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.Listen(protocol, file.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.Rename(file.Name(), addr); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to move temporary file to addr %q: %v", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
|
||||||
|
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
|
||||||
|
protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if protocol != unixProtocol {
|
||||||
|
return "", nil, fmt.Errorf("only support unix socket endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, dial, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
return (&net.Dialer{}).DialContext(ctx, unixProtocol, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEndpointWithFallbackProtocol(endpoint string, fallbackProtocol string) (protocol string, addr string, err error) {
|
||||||
|
if protocol, addr, err = parseEndpoint(endpoint); err != nil && protocol == "" {
|
||||||
|
fallbackEndpoint := fallbackProtocol + "://" + endpoint
|
||||||
|
protocol, addr, err = parseEndpoint(fallbackEndpoint)
|
||||||
|
if err == nil {
|
||||||
|
klog.Warningf("Using %q as endpoint is deprecated, please consider using full url format %q.", endpoint, fallbackEndpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEndpoint(endpoint string) (string, string, error) {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "tcp":
|
||||||
|
return "tcp", u.Host, nil
|
||||||
|
|
||||||
|
case "unix":
|
||||||
|
return "unix", u.Path, nil
|
||||||
|
|
||||||
|
case "":
|
||||||
|
return "", "", fmt.Errorf("using %q as endpoint is deprecated, please consider using full url format", endpoint)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
|
||||||
|
func IsUnixDomainSocket(filePath string) (bool, error) {
|
||||||
|
fi, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("stat file %s failed: %v", filePath, err)
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSocket == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizePath is a no-op for Linux for now
|
||||||
|
func NormalizePath(path string) string {
|
||||||
|
return path
|
||||||
|
}
|
135
pkg/kubelet/cri/remote/util/util_unix_test.go
Normal file
135
pkg/kubelet/cri/remote/util/util_unix_test.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// +build freebsd linux darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseEndpoint(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
endpoint string
|
||||||
|
expectError bool
|
||||||
|
expectedProtocol string
|
||||||
|
expectedAddr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
endpoint: "unix:///tmp/s1.sock",
|
||||||
|
expectedProtocol: "unix",
|
||||||
|
expectedAddr: "/tmp/s1.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "tcp://localhost:15880",
|
||||||
|
expectedProtocol: "tcp",
|
||||||
|
expectedAddr: "localhost:15880",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe://./pipe/mypipe",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "tcp1://abc",
|
||||||
|
expectedProtocol: "tcp1",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "a b c",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
protocol, addr, err := parseEndpoint(test.endpoint)
|
||||||
|
assert.Equal(t, test.expectedProtocol, protocol)
|
||||||
|
if test.expectError {
|
||||||
|
assert.NotNil(t, err, "Expect error during parsing %q", test.endpoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
assert.Nil(t, err, "Expect no error during parsing %q", test.endpoint)
|
||||||
|
assert.Equal(t, test.expectedAddr, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsUnixDomainSocket(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
listenOnSocket bool
|
||||||
|
expectSocket bool
|
||||||
|
expectError bool
|
||||||
|
invalidFile bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
label: "Domain Socket file",
|
||||||
|
listenOnSocket: true,
|
||||||
|
expectSocket: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Non Existent file",
|
||||||
|
invalidFile: true,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Regular file",
|
||||||
|
listenOnSocket: false,
|
||||||
|
expectSocket: false,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
f, err := ioutil.TempFile("", "test-domain-socket")
|
||||||
|
require.NoErrorf(t, err, "Failed to create file for test purposes: %v while setting up: %s", err, test.label)
|
||||||
|
addr := f.Name()
|
||||||
|
f.Close()
|
||||||
|
var ln *net.UnixListener
|
||||||
|
if test.listenOnSocket {
|
||||||
|
os.Remove(addr)
|
||||||
|
ta, err := net.ResolveUnixAddr("unix", addr)
|
||||||
|
require.NoErrorf(t, err, "Failed to ResolveUnixAddr: %v while setting up: %s", err, test.label)
|
||||||
|
ln, err = net.ListenUnix("unix", ta)
|
||||||
|
require.NoErrorf(t, err, "Failed to ListenUnix: %v while setting up: %s", err, test.label)
|
||||||
|
}
|
||||||
|
fileToTest := addr
|
||||||
|
if test.invalidFile {
|
||||||
|
fileToTest = fileToTest + ".invalid"
|
||||||
|
}
|
||||||
|
result, err := IsUnixDomainSocket(fileToTest)
|
||||||
|
if test.listenOnSocket {
|
||||||
|
// this takes care of removing the file associated with the domain socket
|
||||||
|
ln.Close()
|
||||||
|
} else {
|
||||||
|
// explicitly remove regular file
|
||||||
|
os.Remove(addr)
|
||||||
|
}
|
||||||
|
if test.expectError {
|
||||||
|
assert.NotNil(t, err, "Unexpected nil error from IsUnixDomainSocket for %s", test.label)
|
||||||
|
} else {
|
||||||
|
assert.Nil(t, err, "Unexpected error invoking IsUnixDomainSocket for %s", test.label)
|
||||||
|
}
|
||||||
|
assert.Equal(t, result, test.expectSocket, "Unexpected result from IsUnixDomainSocket: %v for %s", result, test.label)
|
||||||
|
}
|
||||||
|
}
|
55
pkg/kubelet/cri/remote/util/util_unsupported.go
Normal file
55
pkg/kubelet/cri/remote/util/util_unsupported.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// +build !freebsd,!linux,!windows,!darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateListener creates a listener on the specified endpoint.
|
||||||
|
func CreateListener(endpoint string) (net.Listener, error) {
|
||||||
|
return nil, fmt.Errorf("CreateListener is unsupported in this build")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
|
||||||
|
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
|
||||||
|
return "", nil, fmt.Errorf("GetAddressAndDialer is unsupported in this build")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockAndCheckSubPath empty implementation
|
||||||
|
func LockAndCheckSubPath(volumePath, subPath string) ([]uintptr, error) {
|
||||||
|
return []uintptr{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnlockPath empty implementation
|
||||||
|
func UnlockPath(fileHandles []uintptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalEndpoint empty implementation
|
||||||
|
func LocalEndpoint(path, file string) (string, error) {
|
||||||
|
return "", fmt.Errorf("LocalEndpoints are unsupported in this build")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBootTime empty implementation
|
||||||
|
func GetBootTime() (time.Time, error) {
|
||||||
|
return time.Time{}, fmt.Errorf("GetBootTime is unsupported in this build")
|
||||||
|
}
|
149
pkg/kubelet/cri/remote/util/util_windows.go
Normal file
149
pkg/kubelet/cri/remote/util/util_windows.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tcpProtocol = "tcp"
|
||||||
|
npipeProtocol = "npipe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateListener creates a listener on the specified endpoint.
|
||||||
|
func CreateListener(endpoint string) (net.Listener, error) {
|
||||||
|
protocol, addr, err := parseEndpoint(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case tcpProtocol:
|
||||||
|
return net.Listen(tcpProtocol, addr)
|
||||||
|
|
||||||
|
case npipeProtocol:
|
||||||
|
return winio.ListenPipe(addr, nil)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("only support tcp and npipe endpoint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
|
||||||
|
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
|
||||||
|
protocol, addr, err := parseEndpoint(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol == tcpProtocol {
|
||||||
|
return addr, tcpDial, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol == npipeProtocol {
|
||||||
|
return addr, npipeDial, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil, fmt.Errorf("only support tcp and npipe endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpDial(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
return (&net.Dialer{}).DialContext(ctx, tcpProtocol, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func npipeDial(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
return winio.DialPipeContext(ctx, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEndpoint(endpoint string) (string, string, error) {
|
||||||
|
// url.Parse doesn't recognize \, so replace with / first.
|
||||||
|
endpoint = strings.Replace(endpoint, "\\", "/", -1)
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme == "tcp" {
|
||||||
|
return "tcp", u.Host, nil
|
||||||
|
} else if u.Scheme == "npipe" {
|
||||||
|
if strings.HasPrefix(u.Path, "//./pipe") {
|
||||||
|
return "npipe", u.Path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback host if not provided.
|
||||||
|
host := u.Host
|
||||||
|
if host == "" {
|
||||||
|
host = "."
|
||||||
|
}
|
||||||
|
return "npipe", fmt.Sprintf("//%s%s", host, u.Path), nil
|
||||||
|
} else if u.Scheme == "" {
|
||||||
|
return "", "", fmt.Errorf("Using %q as endpoint is deprecated, please consider using full url format", endpoint)
|
||||||
|
} else {
|
||||||
|
return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64")
|
||||||
|
|
||||||
|
// GetBootTime returns the time at which the machine was started, truncated to the nearest second
|
||||||
|
func GetBootTime() (time.Time, error) {
|
||||||
|
currentTime := time.Now()
|
||||||
|
output, _, err := tickCount.Call()
|
||||||
|
if errno, ok := err.(syscall.Errno); !ok || errno != 0 {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
|
||||||
|
func IsUnixDomainSocket(filePath string) (bool, error) {
|
||||||
|
// Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
|
||||||
|
// we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file.
|
||||||
|
|
||||||
|
// Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
|
||||||
|
// for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket
|
||||||
|
// does NOT work in 1809 if the socket file is created within a bind mounted directory by a container
|
||||||
|
// and the FSCTL is issued in the host by the kubelet.
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", filePath)
|
||||||
|
if err == nil {
|
||||||
|
c.Close()
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizePath converts FS paths returned by certain go frameworks (like fsnotify)
|
||||||
|
// to native Windows paths that can be passed to Windows specific code
|
||||||
|
func NormalizePath(path string) string {
|
||||||
|
path = strings.ReplaceAll(path, "/", "\\")
|
||||||
|
if strings.HasPrefix(path, "\\") {
|
||||||
|
path = "c:" + path
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
195
pkg/kubelet/cri/remote/util/util_windows_test.go
Normal file
195
pkg/kubelet/cri/remote/util/util_windows_test.go
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseEndpoint(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
endpoint string
|
||||||
|
expectError bool
|
||||||
|
expectedProtocol string
|
||||||
|
expectedAddr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
endpoint: "unix:///tmp/s1.sock",
|
||||||
|
expectedProtocol: "unix",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "tcp://localhost:15880",
|
||||||
|
expectedProtocol: "tcp",
|
||||||
|
expectedAddr: "localhost:15880",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe://./pipe/mypipe",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectedAddr: "//./pipe/mypipe",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe:////./pipe/mypipe2",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectedAddr: "//./pipe/mypipe2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe:/pipe/mypipe3",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectedAddr: "//./pipe/mypipe3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe:\\\\.\\pipe\\mypipe4",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectedAddr: "//./pipe/mypipe4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "npipe:\\pipe\\mypipe5",
|
||||||
|
expectedProtocol: "npipe",
|
||||||
|
expectedAddr: "//./pipe/mypipe5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "tcp1://abc",
|
||||||
|
expectedProtocol: "tcp1",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: "a b c",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
protocol, addr, err := parseEndpoint(test.endpoint)
|
||||||
|
assert.Equal(t, test.expectedProtocol, protocol)
|
||||||
|
if test.expectError {
|
||||||
|
assert.NotNil(t, err, "Expect error during parsing %q", test.endpoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
require.Nil(t, err, "Expect no error during parsing %q", test.endpoint)
|
||||||
|
assert.Equal(t, test.expectedAddr, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPipe(t *testing.T, label string) {
|
||||||
|
generatePipeName := func(suffixlen int) string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
letter := []rune("abcdef0123456789")
|
||||||
|
b := make([]rune, suffixlen)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letter[rand.Intn(len(letter))]
|
||||||
|
}
|
||||||
|
return "\\\\.\\pipe\\test-pipe" + string(b)
|
||||||
|
}
|
||||||
|
testFile := generatePipeName(4)
|
||||||
|
pipeln, err := winio.ListenPipe(testFile, &winio.PipeConfig{SecurityDescriptor: "D:P(A;;GA;;;BA)(A;;GA;;;SY)"})
|
||||||
|
defer pipeln.Close()
|
||||||
|
|
||||||
|
require.NoErrorf(t, err, "Failed to listen on named pipe for test purposes: %v while setting up: %s", err, label)
|
||||||
|
result, err := IsUnixDomainSocket(testFile)
|
||||||
|
assert.Nil(t, err, "Unexpected error: %v from IsUnixDomainSocket for %s", err, label)
|
||||||
|
assert.False(t, result, "Unexpected result: true from IsUnixDomainSocket: %v for %s", result, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRegularFile(t *testing.T, label string, exists bool) {
|
||||||
|
f, err := ioutil.TempFile("", "test-file")
|
||||||
|
require.NoErrorf(t, err, "Failed to create file for test purposes: %v while setting up: %s", err, label)
|
||||||
|
testFile := f.Name()
|
||||||
|
if !exists {
|
||||||
|
testFile = testFile + ".absent"
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
result, err := IsUnixDomainSocket(testFile)
|
||||||
|
os.Remove(f.Name())
|
||||||
|
assert.Nil(t, err, "Unexpected error: %v from IsUnixDomainSocket for %s", err, label)
|
||||||
|
assert.False(t, result, "Unexpected result: true from IsUnixDomainSocket: %v for %s", result, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUnixDomainSocket(t *testing.T, label string) {
|
||||||
|
f, err := ioutil.TempFile("", "test-domain-socket")
|
||||||
|
require.NoErrorf(t, err, "Failed to create file for test purposes: %v while setting up: %s", err, label)
|
||||||
|
testFile := f.Name()
|
||||||
|
f.Close()
|
||||||
|
os.Remove(testFile)
|
||||||
|
ta, err := net.ResolveUnixAddr("unix", testFile)
|
||||||
|
require.NoErrorf(t, err, "Failed to ResolveUnixAddr: %v while setting up: %s", err, label)
|
||||||
|
unixln, err := net.ListenUnix("unix", ta)
|
||||||
|
require.NoErrorf(t, err, "Failed to ListenUnix: %v while setting up: %s", err, label)
|
||||||
|
result, err := IsUnixDomainSocket(testFile)
|
||||||
|
unixln.Close()
|
||||||
|
assert.Nil(t, err, "Unexpected error: %v from IsUnixDomainSocket for %s", err, label)
|
||||||
|
assert.True(t, result, "Unexpected result: false from IsUnixDomainSocket: %v for %s", result, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsUnixDomainSocket(t *testing.T) {
|
||||||
|
testPipe(t, "Named Pipe")
|
||||||
|
testRegularFile(t, "Regular File that Exists", true)
|
||||||
|
testRegularFile(t, "Regular File that Does Not Exist", false)
|
||||||
|
testUnixDomainSocket(t, "Unix Domain Socket File")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizePath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
originalpath string
|
||||||
|
normalizedPath string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
originalpath: "\\path\\to\\file",
|
||||||
|
normalizedPath: "c:\\path\\to\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "/path/to/file",
|
||||||
|
normalizedPath: "c:\\path\\to\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "/path/to/dir/",
|
||||||
|
normalizedPath: "c:\\path\\to\\dir\\",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "\\path\\to\\dir\\",
|
||||||
|
normalizedPath: "c:\\path\\to\\dir\\",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "/file",
|
||||||
|
normalizedPath: "c:\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "\\file",
|
||||||
|
normalizedPath: "c:\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originalpath: "fileonly",
|
||||||
|
normalizedPath: "fileonly",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
assert.Equal(t, test.normalizedPath, NormalizePath(test.originalpath))
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
rules:
|
|
||||||
# prevent exposing internal api in streaming packages
|
|
||||||
- selectorRegexp: k8s[.]io/kubernetes
|
|
||||||
allowedPrefixes:
|
|
||||||
- k8s.io/kubernetes/pkg/kubelet/server/portforward
|
|
||||||
- k8s.io/kubernetes/pkg/kubelet/server/remotecommand
|
|
@ -1,5 +0,0 @@
|
|||||||
rules:
|
|
||||||
# prevent exposing internal api in streaming packages
|
|
||||||
- selectorRegexp: k8s[.]io/kubernetes
|
|
||||||
forbiddenPrefixes:
|
|
||||||
- ''
|
|
Loading…
Reference in New Issue
Block a user