Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Kubelet PluginWatcher in Windows #81397

Merged
merged 1 commit into from
Aug 28, 2019
Merged

Support Kubelet PluginWatcher in Windows #81397

merged 1 commit into from
Aug 28, 2019

Conversation

ddebroy
Copy link
Member

@ddebroy ddebroy commented Aug 14, 2019

What type of PR is this?

Uncomment only one /kind <> line, hit enter to put that in a new line, and remove leading whitespaces from that line:

/kind api-change
/kind bug
/kind cleanup
/kind design
/kind documentation
/kind failing-test

/kind feature

/kind flake

What this PR does / why we need it:
This PR enhances the kubelet plugin watcher to work with files associated with Unix Domain Sockets on Windows by correctly detecting such files using FSCTL_GET_REPARSE_POINT. In Windows, os.ModeSocket does not get set correctly by golang (golang/go#33357).

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:
Once golang adds support for os.ModeSocket for Windows (golang/go#33357), the function IsUnixDomainSocket introduced here may be removed.

Does this PR introduce a user-facing change?:

Support Kubelet plugin watcher on Windows nodes.

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

- [KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-windows/20190714-windows-csi-support.md

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/feature Categorizes issue or PR as related to a new feature. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Aug 14, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 14, 2019

/sig windows

@k8s-ci-robot k8s-ci-robot added sig/windows Categorizes an issue or PR as relevant to SIG Windows. and removed needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Aug 14, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 14, 2019

/cc @jingxu97 @wk8

@ddebroy ddebroy changed the title Support Pluginwatcher in Windows Support Kubelet plugin watcher in Windows Aug 14, 2019
@k8s-ci-robot k8s-ci-robot added area/kubelet sig/node Categorizes an issue or PR as relevant to SIG Node. labels Aug 14, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 14, 2019

/assign @tallclair @yujuhong

@ddebroy ddebroy changed the title Support Kubelet plugin watcher in Windows Support Kubelet PluginWatcher in Windows Aug 14, 2019
Copy link
Contributor

@mattjmcnaughton mattjmcnaughton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your pr!

Overall, the unix code here looks good to me. I will leave the review of the windows code to someone more experienced in that realm :)

I'm also curious... are we confident this change is the only one necessary to support the plugin watcher in windows? Do we want some form of integration testing behavior responsible for verifying this feature is working on windows? In part, I think the answer to that could relates to how difficult it would be to add e2e tests. Any thoughts on the difficulty?

@ddebroy
Copy link
Member Author

ddebroy commented Aug 14, 2019

@mattjmcnaughton yes this is the key change necessary in the plugin-watcher to get CSI node plugins working end2end based on a prototyping effort. This is discussed in more details in the KEP.

e2e tests will certainly be put in place for Windows based CSI plugins which will exercise this code but that needs a few other pieces under development now. So the e2e test will be a separate PR.

Copy link
Contributor

@pjh pjh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sending this PR!

pkg/kubelet/util/util_windows.go Outdated Show resolved Hide resolved
return true, nil
}
return false, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this return false or an error when the argument is a named pipe?

I'm not sure if that question even totally makes sense, I just know that named pipes are the other mechanism for IPC with Windows Kubernetes and could perhaps see someone mistakenly passing a named pipe path to this method.

Copy link
Member Author

@ddebroy ddebroy Aug 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a named pipe, IsUnixDomainSocket returns false along with a non-nil err as it gets treated similar to an invalid file. Added a unit-test with a named pipe to clarify/check the behavior.

Note that in the context of the plugin watcher/fsnotify, named pipes won't be passed to IsUnixDomainSocket since the watcher is scoped to normal files with the plugin directory

@mattjmcnaughton
Copy link
Contributor

mattjmcnaughton commented Aug 15, 2019 via email

@PatrickLang
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 16, 2019
Copy link
Contributor

@wk8 wk8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nits

@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: strings.ReplaceAll better?

if strings.HasPrefix(socketPath, "\\") {
socketPath = "c:" + socketPath
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe this could be made part of a helper package somewhere? Seems this is the exact same as

func normalizeWindowsPath(path string) string {
normalizedPath := strings.Replace(path, "/", "\\", -1)
if strings.HasPrefix(normalizedPath, "\\") {
normalizedPath = "c:" + normalizedPath
}
return normalizedPath
}
and
func GetWindowsPath(path string) string {
windowsPath := strings.Replace(path, "/", "\\", -1)
if strings.HasPrefix(windowsPath, "\\") {
windowsPath = "c:" + windowsPath
}
return windowsPath
}
.

Maybe that func could live in pkg/kubelet/util/util.go ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for highlighting the common functionality - one note, it makes me a little nervous to define a function in pkg/kubelet/util/util.go that is then imported by pkg/volume and pkg/util/mount.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for factoring the function out. Maybe we should have a pkg/util/windows package.

For this PR, I think we can put the helper function in pkg/kubelet/util so that we don't get more code duplication within kubelet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Moved to pkg/kubelet/util for now.

if strings.HasPrefix(socketPath, "\\") {
socketPath = "c:" + socketPath
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for factoring the function out. Maybe we should have a pkg/util/windows package.

For this PR, I think we can put the helper function in pkg/kubelet/util so that we don't get more code duplication within kubelet.

}
for _, test := range tests {
f, err := ioutil.TempFile("", "test-domain-socket")
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: import github.com/stretchr/testify/require and use require.NoError

Applies to all the unit tests changes in this PR :-)

// Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
// we need to get the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
// for the file (using FSCTL_GET_REPARSE_POINT) and check for reparse tag: reparseTagSocket
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would CreateFile leave any side effect?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a closeHandle call, does it also require a delete or remove file call?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateFile, when invoked with windows.OPEN_EXISTING (as is the case here) returns a handle to an existing file that the deferred CloseHandle will clean up. No other side effects involved. We do not need to delete/remove anything resulting from the CreateFile call as no new file gets created since CreateFile is invoked with windows.OPEN_EXISTING. I will add a comment and explain this to make it clear.

rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
var bytesReturned uint32
// Issue FSCTL_GET_REPARSE_POINT (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-get-reparse-point)
if err = windows.DeviceIoControl(fd, windows.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use err :=

return false, errors.Wrap(err, "FSCTL_GET_REPARSE_POINT failed")
}
rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
if rdb.Header.ReparseTag == reparseTagSocket {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: return (rdb.Header.ReparseTag == reparseTagSocket), nil

@@ -202,6 +208,12 @@ func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
}

func (w *Watcher) handlePluginRegistration(socketPath string) error {
if runtime.GOOS == "windows" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What path format does fsnotify use? Is it just the typical unix /foo/bar/ path?

Copy link
Member Author

@ddebroy ddebroy Aug 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It reports /foo/bar for paths in Linux and \\foo\\bar for paths in Windows.

var pipeln net.Listener
var err error
if test.listenOnPipe {
// generate a random pipe name to listen on
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: define a helper function getRandomPipeName := func(prefix string, len int) string{... may make the test code more readable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

expectSocket: false,
},
}
for _, test := range tests {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.....I think there's too many if/else blocks in the test and the code reuse is not so high... Maybe it'd be easier to write separate test function for each test case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 21, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 21, 2019

@wk8 @yujuhong @jingxu97 @mattjmcnaughton thanks for the comments so far. I have addressed them. PTAL.

@yujuhong
Copy link
Contributor

/approve

Leaving some time for others to chime in.

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ddebroy, yujuhong

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 22, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 23, 2019

/hold
Seeing some odd results when testing. Will remove hold once the investigation into observed failures is complete.

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 23, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 27, 2019

/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 27, 2019
@ddebroy
Copy link
Member Author

ddebroy commented Aug 27, 2019

The reason for the failures observed with the FSCTL logic: when a unix domain socket file is created from within a container (in a path bind-mounted inside the container), the host upon issuing the FSCTL to the file, cannot detect the reparse points associated with unix domain sockets. Most likely the file system filter associated with bind mounting paths in a container is not passing the reparse point data through for files. Switching to a simpler logic that just tries to dial the file works.

@pjh
Copy link
Contributor

pjh commented Aug 27, 2019

I tried the new approach for IsUnixDomainSocket in our CSI prototype setup and it worked for me.

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 27, 2019
Signed-off-by: Deep Debroy <ddebroy@docker.com>
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 27, 2019
@pjh
Copy link
Contributor

pjh commented Aug 27, 2019

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 27, 2019
@PatrickLang
Copy link
Contributor

/retest

@k8s-ci-robot k8s-ci-robot merged commit 08b6737 into kubernetes:master Aug 28, 2019
@k8s-ci-robot k8s-ci-robot added this to the v1.16 milestone Aug 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/kubelet cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/node Categorizes an issue or PR as relevant to SIG Node. sig/windows Categorizes an issue or PR as relevant to SIG Windows. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants