-
Notifications
You must be signed in to change notification settings - Fork 252
test: make WaitUntilNodeReady robust to watch disconnections #7827
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/apimachinery/pkg/api/resource" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/apimachinery/pkg/util/wait" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/apimachinery/pkg/watch" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/client-go/kubernetes" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/client-go/rest" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "k8s.io/client-go/tools/clientcmd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -148,49 +149,54 @@ func (k *Kubeclient) WaitUntilPodRunning(ctx context.Context, namespace string, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (k *Kubeclient) WaitUntilNodeReady(ctx context.Context, t testing.TB, vmssName string) string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer toolkit.LogStepf(t, "waiting for node %s to be ready", vmssName)() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var lastNode *corev1.Node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodes, err := k.Typed.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Logf("error listing nodes: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i := range nodes.Items { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node := &nodes.Items[i] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !strings.HasPrefix(node.Name, vmssName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var lastNode *corev1.Node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for ctx.Err() == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name := func() string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| watcher, err := k.Typed.CoreV1().Nodes().Watch(ctx, metav1.ListOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+156
to
+157
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| watcher, err := k.Typed.CoreV1().Nodes().Watch(ctx, metav1.ListOptions{}) | |
| if err != nil { | |
| nodeList, err := k.Typed.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) | |
| if err != nil { | |
| t.Logf("failed to list nodes: %v, retrying in 5s", err) | |
| select { | |
| case <-ctx.Done(): | |
| case <-time.After(5 * time.Second): | |
| } | |
| return "" | |
| } | |
| for i := range nodeList.Items { | |
| node := &nodeList.Items[i] | |
| if !strings.HasPrefix(node.Name, vmssName) { | |
| continue | |
| } | |
| lastNode = node | |
| for _, cond := range node.Status.Conditions { | |
| if cond.Type == corev1.NodeReady && cond.Status == corev1.ConditionTrue { | |
| t.Logf("node %s is ready", node.Name) | |
| return node.Name | |
| } | |
| } | |
| } | |
| watcher, err := k.Typed.CoreV1().Nodes().Watch(ctx, metav1.ListOptions{ResourceVersion: nodeList.ResourceVersion}) | |
| if err != nil { |
Copilot
AI
Feb 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WaitUntilNodeReady starts a watch and then only reacts to future events. If the node is already present (or already Ready) when the watch begins, there may be no subsequent events and this can block until the context times out. Consider doing an initial Nodes().List/Nodes().Get check (or switching fully to PollUntilContextTimeout + List) to evaluate current state before watching/polling again.
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When watcher.ResultChan() closes (e.g., apiserver closes the connection) or when a watch.Error event occurs, the code immediately re-establishes a new watch with no delay. This can spin in a tight loop and hammer the API server during outages/flaky connections. Add a small backoff (similar to the 5s delay used on watch start failures) before retrying after channel close/error events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description says this was switched to ticker-based polling using List and that the function returns (string, error) instead of calling t.Fatalf, but the implementation still uses Watch and still calls t.Fatalf on failure/deleted events. Either update the implementation to match (List+polling, return error) or adjust the PR description to reflect the actual behavior.