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

Fix race condition in pluginWatcher #93622

Merged

Conversation

knight42
Copy link
Member

@knight42 knight42 commented Aug 1, 2020

Signed-off-by: knight42 anonymousknight96@gmail.com

What type of PR is this?

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

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

/kind flake

What this PR does / why we need it:

Which issue(s) this PR fixes:

Part of #93605
xref: #93605 (comment)

Special notes for your reviewer:

I add some logging with the following patch:

diff --git pkg/kubelet/pluginmanager/pluginwatcher/example_plugin.go pkg/kubelet/pluginmanager/pluginwatcher/example_plugin.go
index d0859cc8967..a67a214556e 100644
--- pkg/kubelet/pluginmanager/pluginwatcher/example_plugin.go
+++ pkg/kubelet/pluginmanager/pluginwatcher/example_plugin.go
@@ -29,9 +29,10 @@ import (
 	"k8s.io/klog/v2"
 
 	registerapi "k8s.io/kubelet/pkg/apis/pluginregistration/v1"
+
 	"k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache"
-	v1beta1 "k8s.io/kubernetes/pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta1"
-	v1beta2 "k8s.io/kubernetes/pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta2"
+	"k8s.io/kubernetes/pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta1"
+	"k8s.io/kubernetes/pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis/v1beta2"
 )
 
 // examplePlugin is a sample plugin to work with plugin watcher
@@ -96,6 +97,7 @@ func GetPluginInfo(plugin *examplePlugin) cache.PluginInfo {
 
 // GetInfo is the RPC invoked by plugin watcher
 func (e *examplePlugin) GetInfo(ctx context.Context, req *registerapi.InfoRequest) (*registerapi.PluginInfo, error) {
+	klog.Errorf("GetInfo called")
 	return &registerapi.PluginInfo{
 		Type:              e.pluginType,
 		Name:              e.pluginName,
@@ -146,6 +148,7 @@ func (e *examplePlugin) Serve(services ...string) error {
 	go func() {
 		defer e.wg.Done()
 		// Blocking call to accept incoming connections.
+		klog.Infof("listening on socket")
 		if err := e.grpcServer.Serve(lis); err != nil {
 			klog.Errorf("example server stopped serving: %v", err)
 		}
diff --git pkg/kubelet/pluginmanager/pluginwatcher/plugin_watcher.go pkg/kubelet/pluginmanager/pluginwatcher/plugin_watcher.go
index 30177f67bdc..e432d399032 100644
--- pkg/kubelet/pluginmanager/pluginwatcher/plugin_watcher.go
+++ pkg/kubelet/pluginmanager/pluginwatcher/plugin_watcher.go
@@ -49,7 +49,7 @@ func NewWatcher(sockDir string, desiredStateOfWorld cache.DesiredStateOfWorld) *
 
 // Start watches for the creation and deletion of plugin sockets at the path
 func (w *Watcher) Start(stopCh <-chan struct{}) error {
-	klog.V(2).Infof("Plugin Watcher Start at %s", w.path)
+	klog.Infof("Plugin Watcher Start at %s", w.path)
 
 	// Creating the directory to be watched if it doesn't exist yet,
 	// and walks through the directory to discover the existing plugins.
@@ -67,11 +67,13 @@ func (w *Watcher) Start(stopCh <-chan struct{}) error {
 	if err := w.traversePluginDir(w.path); err != nil {
 		klog.Errorf("failed to traverse plugin socket path %q, err: %v", w.path, err)
 	}
+	klog.Infof("list pluginDir")
 
 	go func(fsWatcher *fsnotify.Watcher) {
 		for {
 			select {
 			case event := <-fsWatcher.Events:
+				klog.Infof("receive event: %s", event)
 				//TODO: Handle errors by taking corrective measures
 				if event.Op&fsnotify.Create == fsnotify.Create {
 					err := w.handleCreateEvent(event)
@@ -130,6 +132,7 @@ func (w *Watcher) traversePluginDir(dir string) error {
 				Name: path,
 				Op:   fsnotify.Create,
 			}
+			klog.Infof("add existing socket: %s", path)
 			//TODO: Handle errors by taking corrective measures
 			if err := w.handleCreateEvent(event); err != nil {
 				klog.Errorf("error %v when handling create event: %s", err, event)

and find out that if the test fails, the output would be something like

=== RUN   TestPluginRegistration
I0801 11:43:21.268993   74831 example_plugin.go:121] starting example server at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test025788546/plugin.sock
I0801 11:43:21.269004   74831 plugin_watcher.go:52] Plugin Watcher Start at /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test025788546
I0801 11:43:21.269916   74831 example_plugin.go:127] example server started at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test025788546/plugin.sock
I0801 11:43:21.270146   74831 example_plugin.go:151] listening on socket
I0801 11:43:21.270248   74831 plugin_watcher.go:70] list pluginDir
I0801 11:43:21.270288   74831 plugin_manager.go:114] Starting Kubelet Plugin Manager # <---- stuck here
    TestPluginRegistration: plugin_manager_test.go:107: Timed out waiting for plugin to be added to actual state of world cache.
I0801 11:44:21.783013   74831 plugin_manager.go:119] Shutting down Kubelet Plugin Manager
--- FAIL: TestPluginRegistration (60.51s)
FAIL
exit status 1
FAIL    k8s.io/kubernetes/pkg/kubelet/pluginmanager     60.851s

while if the test passes, the output would be sth like

=== RUN   TestPluginRegistration
I0801 11:43:18.278273   74796 example_plugin.go:121] starting example server at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test291123507/plugin.sock
I0801 11:43:18.278302   74796 plugin_watcher.go:52] Plugin Watcher Start at /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test291123507
I0801 11:43:18.279166   74796 example_plugin.go:127] example server started at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test291123507/plugin.sock
I0801 11:43:18.279389   74796 example_plugin.go:151] listening on socket
I0801 11:43:18.279658   74796 plugin_watcher.go:135] add existing socket: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test291123507/plugin.sock
I0801 11:43:18.279708   74796 plugin_watcher.go:70] list pluginDir
I0801 11:43:18.279730   74796 plugin_manager.go:114] Starting Kubelet Plugin Manager
E0801 11:43:18.280703   74796 example_plugin.go:100] GetInfo called
E0801 11:43:18.280951   74796 example_plugin.go:110] Registration is: &RegistrationStatus{PluginRegistered:true,Error:,}
I0801 11:43:18.779723   74796 plugin_manager.go:119] Shutting down Kubelet Plugin Manager
--- PASS: TestPluginRegistration (0.50s)

Or

=== RUN   TestPluginRegistration
I0801 11:43:13.759376   74763 example_plugin.go:121] starting example server at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test526537534/plugin.sock
I0801 11:43:13.759538   74763 plugin_watcher.go:52] Plugin Watcher Start at /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test526537534
I0801 11:43:13.760092   74763 plugin_watcher.go:70] list pluginDir
I0801 11:43:13.760104   74763 plugin_manager.go:114] Starting Kubelet Plugin Manager
I0801 11:43:13.760137   74763 example_plugin.go:127] example server started at: /var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test526537534/plugin.sock
I0801 11:43:13.760198   74763 example_plugin.go:151] listening on socket
I0801 11:43:13.760342   74763 plugin_watcher.go:76] receive event: "/var/folders/bp/20yxytt90055krc_0hct5qrm0000gn/T/plugin_manager_test526537534/plugin.sock": CREATE
E0801 11:43:14.761989   74763 example_plugin.go:100] GetInfo called
E0801 11:43:14.762267   74763 example_plugin.go:110] Registration is: &RegistrationStatus{PluginRegistered:true,Error:,}
I0801 11:43:15.764041   74763 plugin_manager.go:119] Shutting down Kubelet Plugin Manager
--- PASS: TestPluginRegistration (2.01s)

EDIT:
See also #93622 (comment)
I think the root cause is more likely to be concurrently creating the socket and adding the plugin dir to w.fsWatcher in the test. I guess the execution order might be:

  1. The plugin socket has not been created, plugin watcher is traversing the plugin dir, just read dir names but not call walkFn yet https://github.com/golang/go/blob/6f08e89ec3280bf6577c2bdb01243cbeeb1a259d/src/path/filepath/path.go#L358-L364, so plugin watcher would not find the socket later
  2. The plugin socket is created
  3. walkFn is invoked and the plugin dir is added to the watcher, so the watcher would not receive any events later.

I decided to watch the socket dir before traversing it.

  1. if the socket is created before we watching the dir, then we would find the socket while we are traversing the socket dir
  2. if the socket is created after we watching the dir, then we would receive the CREATE event.

Please notice when the test fails, the plugin watcher could neither find any sockets in the socket dir nor receive "CREATE" event later. I suspect fsnotify is not extremely reliable and might miss events in some cases.

As a result, I decide to make sure the example plugin server is started before plugin manager, so that the socket always exists when the plugin manager is started. I have run the test 100 times locally after this change, they all pass.

Does this PR introduce a user-facing change?:

kubelet: fix race condition in pluginWatcher 

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


@k8s-ci-robot k8s-ci-robot added release-note-none Denotes a PR that doesn't merit a release note. kind/flake Categorizes issue or PR as related to a flaky test. size/S Denotes a PR that changes 10-29 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. area/kubelet sig/node Categorizes an issue or PR as relevant to SIG Node. and removed needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Aug 1, 2020
@knight42 knight42 mentioned this pull request Aug 1, 2020
37 tasks
@liggitt
Copy link
Member

liggitt commented Aug 1, 2020

/retest

go func() {
sourcesReady := config.NewSourcesReady(func(_ sets.String) bool { return true })
<-pluginServing
pluginManager.Run(sourcesReady, stopChan)
Copy link
Member

@liggitt liggitt Aug 1, 2020

Choose a reason for hiding this comment

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

how is this different than simply moving this goroutine after the p.Serve call where the close(pluginServing) call is?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point! Updated as you said.

@knight42 knight42 force-pushed the test/plugin-register-timeout branch from e5cfa79 to 86c5849 Compare August 2, 2020 03:30
@k8s-ci-robot k8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Aug 2, 2020
@knight42
Copy link
Member Author

knight42 commented Aug 2, 2020

/retest

@liggitt liggitt added this to the v1.19 milestone Aug 3, 2020
@liggitt
Copy link
Member

liggitt commented Aug 3, 2020

/cc @msau42
since you've reviewed changes in the plugin manager

@dashpole
Copy link
Contributor

dashpole commented Aug 3, 2020

/lgtm
/approve
Thanks for the fix!

@k8s-ci-robot k8s-ci-robot added lgtm "Looks good to me", indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Aug 3, 2020
@@ -140,13 +137,18 @@ func TestPluginRegistration(t *testing.T) {
p := pluginwatcher.NewTestExamplePlugin(pluginName, registerapi.DevicePlugin, socketPath, supportedVersions...)
require.NoError(t, p.Serve("v1beta1", "v1beta2"))

// Ensure example plugin is started before plugin manager, so that the socket is present
Copy link
Member

Choose a reason for hiding this comment

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

/hold

If fsnotify is flaky and can miss events, then that seems like a product issue, where we need to design the plugin manager to be able to handle that?

Having the test start the plugin before the plugin manager seems like a workaround to get the test to pass, but masks potentially real problems. In a real system, the plugin is usually started after the plugin manager.

Copy link
Member

Choose a reason for hiding this comment

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

I think what was racing here was not the filesystem and pluginmanager, but the observation of the plugin and the registration of the handler

in real life, we add the handlers that are sent the registration events before calling pluginManager.Run()

// Adding Registration Callback function for CSI Driver
kl.pluginManager.AddHandler(pluginwatcherapi.CSIPlugin, plugincache.PluginHandler(csi.PluginHandler))
// Adding Registration Callback function for Device Manager
kl.pluginManager.AddHandler(pluginwatcherapi.DevicePlugin, kl.containerManager.GetPluginRegistrationHandler())
// Start the plugin manager
klog.V(4).Infof("starting plugin manager")
go kl.pluginManager.Run(kl.sourcesReady, wait.NeverStop)

would moving this above the goroutine where we call pluginManager.Run(sourcesReady, stopChan) be sufficient?

fakeHandler := newFakePluginHandler()
pluginManager.AddHandler(registerapi.DevicePlugin, fakeHandler)

Copy link
Member

Choose a reason for hiding this comment

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

hmm, just saw in the PR description:

Please notice when the test fails, the plugin watcher could neither find any sockets in the socket dir nor receive "CREATE" event later. I suspect fsnotify is not extremely reliable and might miss events in some cases.

As a result, I decide to make sure the example plugin server is started before plugin manager, so that the socket always exists when the plugin manager is started. I have run the test 100 times locally after this change, they all pass.

that's troubling, and I agree with @msau42 that if that is the issue we need a real fix, not to mask it in a test

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, let me try creating a minimal reproducible example first.

@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 3, 2020
@liggitt
Copy link
Member

liggitt commented Aug 4, 2020

/lgtm cancel

@knight42 knight42 requested a review from liggitt August 13, 2020 13:29
require.NoError(t, p.Serve("v1beta1", "v1beta2"))

// Verify that the plugin is registered
waitForRegistration(t, fakeHandler)
Copy link
Member

Choose a reason for hiding this comment

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

waitForRegistration only verifies a single plugin was registered, so after the first one at depth 0, it is not checking anything further... we need to pass in the particular path we're expecting to be registered, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

we don't need to pass the particular path, we just need to reset the handler.

Copy link
Member

Choose a reason for hiding this comment

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

we don't need to pass the particular path, we just need to reset the handler.

it would be better to check the specific path, otherwise a late/duplicate event from an early plugin could make it look like a later one passed

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually a late event would not affect the test, as the test would fail immediately if registration timeout, and I think duplicate event rarely occurs on a local network. But I think this check is good to have.

@liggitt
Copy link
Member

liggitt commented Aug 13, 2020

the change in traversePluginDir looks good to me, though I'd like @msau42 or @jsafrane to look at it closely as well

just a couple comments on the test, and I wanted to check if you ran the whole pluginmanager package with the stress utility as described in #93902 to make sure this resolves all races and doesn't introduce new ones

@knight42 knight42 requested a review from liggitt August 13, 2020 14:22
@knight42
Copy link
Member Author

$ stress ./pluginmanager.test -test.run TestPluginRegistration
0 runs so far, 0 failures
19 runs so far, 0 failures
34 runs so far, 0 failures
50 runs so far, 0 failures
63 runs so far, 0 failures
81 runs so far, 0 failures
95 runs so far, 0 failures
109 runs so far, 0 failures
125 runs so far, 0 failures
141 runs so far, 0 failures
154 runs so far, 0 failures
169 runs so far, 0 failures
186 runs so far, 0 failures
198 runs so far, 0 failures
211 runs so far, 0 failures
224 runs so far, 0 failures
243 runs so far, 0 failures
256 runs so far, 0 failures
270 runs so far, 0 failures
284 runs so far, 0 failures
300 runs so far, 0 failures
316 runs so far, 0 failures
330 runs so far, 0 failures
346 runs so far, 0 failures
362 runs so far, 0 failures
378 runs so far, 0 failures
390 runs so far, 0 failures
406 runs so far, 0 failures
421 runs so far, 0 failures
437 runs so far, 0 failures
451 runs so far, 0 failures
468 runs so far, 0 failures
481 runs so far, 0 failures
495 runs so far, 0 failures
511 runs so far, 0 failures
527 runs so far, 0 failures
540 runs so far, 0 failures
553 runs so far, 0 failures
572 runs so far, 0 failures
587 runs so far, 0 failures
602 runs so far, 0 failures
616 runs so far, 0 failures
633 runs so far, 0 failures
648 runs so far, 0 failures
662 runs so far, 0 failures
673 runs so far, 0 failures

@liggitt
Copy link
Member

liggitt commented Aug 13, 2020

now that our fake handler is handling multiple plugins, I think we should record the actual order and plugin names we encounter, like this (otherwise, the last event in will stomp evidence of unexpected calls from other plugins):

diff --git a/pkg/kubelet/pluginmanager/plugin_manager_test.go b/pkg/kubelet/pluginmanager/plugin_manager_test.go
index d81f0d42e83..14dd5387e12 100644
--- a/pkg/kubelet/pluginmanager/plugin_manager_test.go
+++ b/pkg/kubelet/pluginmanager/plugin_manager_test.go
@@ -21,6 +21,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strconv"
 	"sync"
 	"testing"
@@ -42,26 +43,19 @@ var (
 )
 
 type fakePluginHandler struct {
-	validatePluginCalled   bool
-	registerPluginCalled   bool
-	deregisterPluginCalled bool
-	registerEndpoint       string
+	events []string
 	sync.RWMutex
 }
 
 func newFakePluginHandler() *fakePluginHandler {
-	return &fakePluginHandler{
-		validatePluginCalled:   false,
-		registerPluginCalled:   false,
-		deregisterPluginCalled: false,
-	}
+	return &fakePluginHandler{}
 }
 
 // ValidatePlugin is a fake method
 func (f *fakePluginHandler) ValidatePlugin(pluginName string, endpoint string, versions []string) error {
 	f.Lock()
 	defer f.Unlock()
-	f.validatePluginCalled = true
+	f.events = append(f.events, fmt.Sprintf("validate %s", pluginName))
 	return nil
 }
 
@@ -69,8 +63,7 @@ func (f *fakePluginHandler) ValidatePlugin(pluginName string, endpoint string, v
 func (f *fakePluginHandler) RegisterPlugin(pluginName, endpoint string, versions []string) error {
 	f.Lock()
 	defer f.Unlock()
-	f.registerPluginCalled = true
-	f.registerEndpoint = endpoint
+	f.events = append(f.events, fmt.Sprintf("register %s", pluginName))
 	return nil
 }
 
@@ -78,11 +71,13 @@ func (f *fakePluginHandler) RegisterPlugin(pluginName, endpoint string, versions
 func (f *fakePluginHandler) DeRegisterPlugin(pluginName string) {
 	f.Lock()
 	defer f.Unlock()
-	f.deregisterPluginCalled = true
+	f.events = append(f.events, fmt.Sprintf("deregister %s", pluginName))
 }
 
 func (f *fakePluginHandler) Reset() {
-	*f = fakePluginHandler{}
+	f.Lock()
+	defer f.Unlock()
+	f.events = nil
 }
 
 func init() {
@@ -99,15 +94,18 @@ func cleanup(t *testing.T) {
 	os.MkdirAll(socketDir, 0755)
 }
 
-func waitForRegistration(t *testing.T, fakePluginHandler *fakePluginHandler, expectedEndpoint string) {
+func waitForRegistration(t *testing.T, fakePluginHandler *fakePluginHandler, pluginName string) {
 	err := retryWithExponentialBackOff(
-		500*time.Millisecond,
+		100*time.Millisecond,
 		func() (bool, error) {
 			fakePluginHandler.Lock()
 			defer fakePluginHandler.Unlock()
-			return fakePluginHandler.validatePluginCalled &&
-				fakePluginHandler.registerPluginCalled &&
-				fakePluginHandler.registerEndpoint == expectedEndpoint, nil
+			expected := []string{"validate " + pluginName, "register " + pluginName}
+			if reflect.DeepEqual(fakePluginHandler.events, expected) {
+				return true, nil
+			}
+			t.Logf("expected %#v, got %#v, will retry", expected, fakePluginHandler.events)
+			return false, nil
 		},
 	)
 	if err != nil {
@@ -160,7 +158,7 @@ func TestPluginRegistration(t *testing.T) {
 		require.NoError(t, p.Serve("v1beta1", "v1beta2"))
 
 		// Verify that the plugin is registered
-		waitForRegistration(t, fakeHandler, socketPath)
+		waitForRegistration(t, fakeHandler, pluginName)
 	}
 }

(I also shortened the initial backoff time to 100ms which shortened the test from ~8 seconds to ~4)

stress seems happy with it as well:

stress ./pluginmanager.test -test.run TestPluginRegistration
0 runs so far, 0 failures
24 runs so far, 0 failures
36 runs so far, 0 failures
48 runs so far, 0 failures
60 runs so far, 0 failures
73 runs so far, 0 failures
90 runs so far, 0 failures
108 runs so far, 0 failures
120 runs so far, 0 failures
132 runs so far, 0 failures
147 runs so far, 0 failures
164 runs so far, 0 failures
180 runs so far, 0 failures
192 runs so far, 0 failures
204 runs so far, 0 failures
218 runs so far, 0 failures
235 runs so far, 0 failures
250 runs so far, 0 failures
264 runs so far, 0 failures
277 runs so far, 0 failures
291 runs so far, 0 failures
308 runs so far, 0 failures
321 runs so far, 0 failures
336 runs so far, 0 failures
348 runs so far, 0 failures
362 runs so far, 0 failures
378 runs so far, 0 failures
393 runs so far, 0 failures
407 runs so far, 0 failures
420 runs so far, 0 failures
433 runs so far, 0 failures
450 runs so far, 0 failures
465 runs so far, 0 failures
480 runs so far, 0 failures
492 runs so far, 0 failures
507 runs so far, 0 failures
523 runs so far, 0 failures
537 runs so far, 0 failures
552 runs so far, 0 failures
564 runs so far, 0 failures

@liggitt
Copy link
Member

liggitt commented Aug 14, 2020

/hold
until updates are 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 14, 2020
@liggitt liggitt changed the title Deflake TestPluginRegistration test Fix race condition in pluginWatcher Aug 14, 2020
@liggitt liggitt added kind/bug Categorizes issue or PR as related to a bug. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. labels Aug 14, 2020
@k8s-ci-robot k8s-ci-robot removed the needs-priority Indicates a PR lacks a `priority/foo` label and requires one. label Aug 14, 2020
@liggitt liggitt removed the kind/flake Categorizes issue or PR as related to a flaky test. label Aug 14, 2020
Signed-off-by: knight42 <anonymousknight96@gmail.com>
@knight42 knight42 force-pushed the test/plugin-register-timeout branch from 6b6f510 to de46e81 Compare August 14, 2020 04:07
@knight42
Copy link
Member Author

@liggitt Please take another look

@liggitt
Copy link
Member

liggitt commented Aug 14, 2020

/lgtm

would like @msau42 to take a look as well before removing the hold

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 14, 2020
@liggitt
Copy link
Member

liggitt commented Aug 14, 2020

/retest

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-none Denotes a PR that doesn't merit a release note. labels Aug 14, 2020
@msau42
Copy link
Member

msau42 commented Aug 17, 2020

/assign @saad-ali
cc @chrishenzie

Copy link
Member

@saad-ali saad-ali left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve
/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 18, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dashpole, knight42, saad-ali

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

@liggitt
Copy link
Member

liggitt commented Aug 18, 2020

/retest
/skip

@k8s-ci-robot k8s-ci-robot merged commit fd74333 into kubernetes:master Aug 18, 2020
@knight42 knight42 deleted the test/plugin-register-timeout branch August 18, 2020 02:34
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/bug Categorizes issue or PR as related to a bug. lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. 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. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants