Skip to content
Permalink
Browse files

Support PluginWatcher in Windows

Signed-off-by: Deep Debroy <ddebroy@docker.com>
  • Loading branch information...
ddebroy committed Aug 13, 2019
1 parent f2c82f4 commit 1e26ca7c9dd15480eba058663e2a831983d46070
@@ -14,6 +14,7 @@ go_library(
"//pkg/kubelet/pluginmanager/cache:go_default_library",
"//pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta1:go_default_library",
"//pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta2:go_default_library",
"//pkg/kubelet/util:go_default_library",
"//pkg/util/filesystem:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
@@ -20,13 +20,15 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/fsnotify/fsnotify"
"k8s.io/klog"

"k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache"
"k8s.io/kubernetes/pkg/kubelet/util"
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)

@@ -190,7 +192,11 @@ func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
}

if !fi.IsDir() {
if fi.Mode()&os.ModeSocket == 0 {
isSocket, err := util.IsUnixDomainSocket(fi, event.Name)
if err != nil {
return fmt.Errorf("failed to determine if file: %s is a unix domain socket: %v", event.Name, err)
}
if !isSocket {
klog.V(5).Infof("Ignoring non socket file %s", fi.Name())
return nil
}
@@ -202,6 +208,12 @@ func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
}

func (w *Watcher) handlePluginRegistration(socketPath string) error {
if runtime.GOOS == "windows" {
socketPath = strings.Replace(socketPath, "/", "\\", -1)
if strings.HasPrefix(socketPath, "\\") {
socketPath = "c:" + socketPath
}
}
//TODO: Implement rate limiting to mitigate any DOS kind of attacks.
// Update desired state of world list of plugins
// If the socket path does exist in the desired world cache, there's still
@@ -60,6 +60,8 @@ go_library(
],
"@io_bazel_rules_go//go/platform:windows": [
"//vendor/github.com/Microsoft/go-winio:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/sys/windows:go_default_library",
],
"//conditions:default": [],
}),
@@ -135,3 +135,14 @@ func LocalEndpoint(path, file string) (string, error) {
}
return filepath.Join(u.String(), file+".sock"), nil
}

// IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
func IsUnixDomainSocket(fileInfo os.FileInfo, filePath string) (bool, error) {
if fileInfo == nil {
return false, fmt.Errorf("nil FileInfo passed")
}
if fileInfo.Mode()&os.ModeSocket == 0 {
return false, nil
}
return true, nil
}
@@ -19,6 +19,9 @@ limitations under the License.
package util

import (
"io/ioutil"
"net"
"os"
"testing"

"github.com/stretchr/testify/assert"
@@ -69,3 +72,63 @@ func TestParseEndpoint(t *testing.T) {
}

}

func TestIsUnixDomainSocket(t *testing.T) {
tests := []struct {
label string
listenOnSocket bool
expectSocket bool
expectError bool
}{
{
label: "Domain Socket file",
listenOnSocket: true,
expectSocket: true,
expectError: false,
},
{
label: "Regular file",
listenOnSocket: false,
expectSocket: false,
expectError: false,
},
}
for _, test := range tests {
f, err := ioutil.TempFile("", "test-domain-socket")
if err != nil {
t.Fatalf("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)
if err != nil {
t.Fatalf("Failed to ResolveUnixAddr: %v while setting up: %s", err, test.label)
}
ln, err = net.ListenUnix("unix", ta)
if err != nil {
t.Fatalf("Failed to ListenUnix: %v while setting up: %s", err, test.label)
}
}
fi, err := os.Stat(addr)
if err != nil {
t.Fatalf("Failed to stat file %s: %v", addr, err)
}
result, err := IsUnixDomainSocket(fi, addr)
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)
}
}
@@ -22,16 +22,24 @@ import (
"fmt"
"net"
"net/url"
"os"
"strings"
"syscall"
"time"
"unsafe"

"github.com/Microsoft/go-winio"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)

const (
tcpProtocol = "tcp"
npipeProtocol = "npipe"

reparseTagSocket = 0x80000023

msgNotAReparsePoint = "The file or directory is not a reparse point."
)

// CreateListener creates a listener on the specified endpoint.
@@ -123,3 +131,37 @@ func GetBootTime() (time.Time, error) {
}
return currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second), nil
}

type reparseDataBufferHeader struct {
ReparseTag uint32
ReparseDataLength uint16
Reserved uint16
}

type reparseDataBuffer struct {
Header reparseDataBufferHeader
Detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
}

// IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
func IsUnixDomainSocket(fileInfo os.FileInfo, filePath string) (bool, error) {
fd, err := windows.CreateFile(windows.StringToUTF16Ptr(filePath), windows.GENERIC_READ, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OPEN_REPARSE_POINT|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
if err != nil {
return false, errors.Wrap(err, "CreateFile failed")
}
defer windows.CloseHandle(fd)

rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
var bytesReturned uint32
if err = windows.DeviceIoControl(fd, windows.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil {
if err.Error() == msgNotAReparsePoint {
return false, nil
}
return false, errors.Wrap(err, "FSCTL_GET_REPARSE_POINT failed")
}
rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
if rdb.Header.ReparseTag == reparseTagSocket {
return true, nil
}
return false, nil
}
@@ -19,6 +19,9 @@ limitations under the License.
package util

import (
"io/ioutil"
"net"
"os"
"testing"

"github.com/stretchr/testify/assert"
@@ -90,3 +93,70 @@ func TestParseEndpoint(t *testing.T) {
}

}

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")
if err != nil {
t.Fatalf("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)
if err != nil {
t.Fatalf("Failed to ResolveUnixAddr: %v while setting up: %s", err, test.label)
}
ln, err = net.ListenUnix("unix", ta)
if err != nil {
t.Fatalf("Failed to ListenUnix: %v while setting up: %s", err, test.label)
}
}
var fi os.FileInfo
fileToTest := addr
if test.invalidFile {
fileToTest = fileToTest + ".invalid"
}
result, err := IsUnixDomainSocket(fi, 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)
}
}

0 comments on commit 1e26ca7

Please sign in to comment.
You can’t perform that action at this time.