test: tolerate cloud-provider-uninitialized taint on Windows validation pods - #9204
Conversation
…n pods servercore/nanoserver validation pods (podWindows) had no tolerations, so if the node was Ready but the Azure CCM hadn't yet removed the node.cloudprovider.kubernetes.io/uninitialized:NoSchedule taint, the pod stayed Pending until the 360s wait timed out, failing the test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Windows Unit Test Results 3 files 12 suites 59s ⏱️ Results for commit ed3d843. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Pull request overview
This PR updates the Windows E2E helper pod spec so Windows validation pods can schedule even when the target node is still tainted with node.cloudprovider.kubernetes.io/uninitialized:NoSchedule, avoiding false-negative timeouts during Windows image validation.
Changes:
- Add an explicit pod toleration for the
node.cloudprovider.kubernetes.io/uninitialized:NoScheduletaint inpodWindows(). - Document why the toleration is needed (node may report Ready before CCM removes the taint).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
e2e/kube.go:786
- PR description says to add a toleration to podWindows(), but podWindows() has been removed and replaced with a DaemonSet helper (debugDaemonsetWindows). If the intent is still just to tolerate the cloud-provider-uninitialized taint for the Windows validation pod, consider restoring a Pod-based helper and adding the toleration there (or update the PR description to match the DaemonSet approach and add corresponding DaemonSet validation/cleanup).
func debugDaemonsetWindows(s *Scenario, podName string, imageName string) *appsv1.DaemonSet {
deploymentName := fmt.Sprintf("%s-test-%s-pod", s.Runtime.VM.KubeName, podName)
return &appsv1.DaemonSet{
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
APIVersion: "apps/v1",
},
e2e/kube.go:809
- Typo in comment: "nanoserve" should be "nanoserver".
// this should exist on both servercore and nanoserve
|
I traced the recurring failure to the Windows pause image. Build
This test uses Please replace the toleration with a compatibility fix:
The validation-pod toleration weakens the readiness check without repairing node initialization. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
e2e/kube.go:474
- This adds a toleration for the cloud-provider-uninitialized taint inside getPodTolerations(), and that helper is now used by podHTTPServerLinux() and daemonsetDebug() too. This broadens behavior beyond the PR description/title (Windows validation pods) and may mask issues by allowing those other workloads to schedule before CCM taint removal. Consider splitting helpers (e.g., base test-taint tolerations vs. Windows validation tolerations that additionally include the uninitialized taint) and use the uninitialized toleration only where needed.
{
Key: "node.cloudprovider.kubernetes.io/uninitialized",
Operator: corev1.TolerationOpExists,
Effect: corev1.TaintEffectNoSchedule,
},
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
e2e/kube.go:777
debugPodWindowscreates a Pod but setsTypeMetatoKind: DaemonSet/APIVersion: apps/v1. This can lead to API server decoding/validation issues for the Pod create request (and is misleading even if ignored).
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
APIVersion: "apps/v1",
},
e2e/kube.go:758
- The comment above
podHTTPServerLinuxstill describes tolerating only the test taints, but the pod now usesgetPodTolerations()which also includes the cloud-provider-uninitialized toleration. This is now inaccurate.
// Set Tolerations to tolerate the node with test taints "testkey1=value1:NoSchedule,testkey2=value2:NoSchedule".
// This is to ensure that the pod can be scheduled on the node with the taints.
// It won't affect other pods running on the same node.
e2e/kube.go:475
- PR description says the change is limited to Windows validation pods, but
getPodTolerations()is now used by Linux validation pods anddaemonsetDebug()as well, meaning the newnode.cloudprovider.kubernetes.io/uninitializedtoleration applies more broadly. Please confirm this is intended and either update the PR description or scope the toleration to the Windows validation pods only.
{
Key: "node.cloudprovider.kubernetes.io/uninitialized",
Operator: corev1.TolerationOpExists,
Effect: corev1.TaintEffectNoSchedule,
},
}
e2e/kube.go:456
- The comment on
getPodTolerationssays it only tolerates the test taints, but the function now also toleratesnode.cloudprovider.kubernetes.io/uninitialized. Please update the comment so it matches behavior.
This issue also appears in the following locations of the same file:
- line 470
- line 756
// Set Tolerations to tolerate the node with test taints "testkey1=value1:NoSchedule,testkey2=value2:NoSchedule".
// This is to ensure that the pod can be scheduled on the node with the taints.
// It won't affect other pods running on the same node.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (3)
e2e/validation.go:29
- The retry sleep/backoff block has a few issues: the comment misspells "exponential" and doesn't match the actual sequence (currently 1s, 2s, 4s…), the log prints a time.Duration with %d while claiming "seconds", and it sleeps even after the final attempt (adding unnecessary delay before failing). Consider computing an explicit seconds integer, only sleeping when another retry remains, and updating the comment accordingly.
// crude expenential backoff: 2s, 4s, 8s, ...
retryBackoff := time.Duration(1 << uint(i))
if err != nil {
s.T.Logf("sleeping %d seconds before retrying pod %q", retryBackoff, pod.Name)
time.Sleep(retryBackoff * time.Second)
e2e/kube.go:475
- PR description/title indicate adding the cloud-provider-uninitialized toleration specifically for the Windows validation pod, but it’s currently included in getPodTolerations(), which is also used by podHTTPServerLinux() and daemonsetDebug(). That broadens the behavioral change to Linux validation and debug workloads too. If the broader scope is intended, update the PR description and the getPodTolerations() comment to reflect it; if it’s not intended, consider keeping test taint tolerations shared and adding the uninitialized toleration only in the Windows pod builder.
func getPodTolerations() []corev1.Toleration {
// Set Tolerations to tolerate the node with test taints "testkey1=value1:NoSchedule,testkey2=value2:NoSchedule".
// This is to ensure that the pod can be scheduled on the node with the taints.
// It won't affect other pods running on the same node.
return []corev1.Toleration{
{
Key: "testkey1",
Operator: corev1.TolerationOpEqual,
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
},
{
Key: "testkey2",
Operator: corev1.TolerationOpEqual,
Value: "value2",
Effect: corev1.TaintEffectNoSchedule,
},
{
Key: "node.cloudprovider.kubernetes.io/uninitialized",
Operator: corev1.TolerationOpExists,
Effect: corev1.TaintEffectNoSchedule,
},
}
e2e/kube.go:773
- debugPodWindows() is used as the primary Windows validation pod builder (ValidateNodeCanRunAPod), so the "debug" prefix is misleading. Consider renaming back to podWindows()/validationPodWindows() (and similarly rename the local "deploymentName" variable, since this is a Pod name) to make intent clearer for future maintainers.
func debugPodWindows(s *Scenario, podName string, imageName string) *corev1.Pod {
deploymentName := fmt.Sprintf("%s-test-%s-pod", s.Runtime.VM.KubeName, podName)
return &corev1.Pod{
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (5)
e2e/kube.go:760
- This comment says the pod only tolerates the synthetic test taints, but the pod now uses
getPodTolerations()which also includes thenode.cloudprovider.kubernetes.io/uninitialized:NoScheduletoleration. Please update the comment so it reflects the actual scheduling behavior.
// Set Tolerations to tolerate the node with test taints "testkey1=value1:NoSchedule,testkey2=value2:NoSchedule".
// This is to ensure that the pod can be scheduled on the node with the taints.
// It won't affect other pods running on the same node.
Tolerations: getPodTolerations(),
NodeSelector: getNodeSelectorForScenario(s),
e2e/kube.go:456
- The comment above
getPodTolerationsno longer matches what the function returns: it now also toleratesnode.cloudprovider.kubernetes.io/uninitialized:NoSchedule, but the comment only mentions the synthetic test taints. Please update the comment to include the uninitialized taint so readers don’t miss the scheduling behavior change.
This issue also appears on line 756 of the same file.
// Set Tolerations to tolerate the node with test taints "testkey1=value1:NoSchedule,testkey2=value2:NoSchedule".
// This is to ensure that the pod can be scheduled on the node with the taints.
// It won't affect other pods running on the same node.
e2e/validation.go:29
- The retry loop is off-by-one and can log impossible attempt counts (e.g., 4/3) because
istarts at 1, the loop condition isi <= maxRetries, and the log usesi+1. This also changes semantics from “max attempts” to “max retries”, which can significantly extend E2E runtime since each attempt can block up to 6 minutes in WaitUntilPodRunning. Consider rewriting this as a clear attempt-based loop (maxAttempts) and using context-aware backoff sleep; also fix the "expenential" typo while touching this block.
for i <= maxRetries && err != nil {
// crude expenential backoff: 2s, 4s, 8s, ...
retryBackoff := time.Duration(1 << uint(i))
s.T.Logf("sleeping %d seconds before retrying pod %q", retryBackoff, pod.Name)
time.Sleep(retryBackoff * time.Second)
e2e/kube.go:789
- Typo in comment: "nanoserve" should be "nanoserver".
ImagePullPolicy: "IfNotPresent",
// this should exist on both servercore and nanoserve
Command: []string{"cmd", "/c", "ping", "-t", "localhost"},
e2e/kube.go:772
deploymentNameis used as a Pod name here, not a Deployment name. Renaming it to something likepodFullNameavoids confusion when debugging Kubernetes objects.
deploymentName := fmt.Sprintf("%s-test-%s-pod", s.Runtime.VM.KubeName, podName)
Summary
Windows E2E validation pods (
podWindows, used for the servercore/nanoserver checks) had no tolerations. If the target node reportedReady=Truebefore the Azure cloud-controller-manager removed itsnode.cloudprovider.kubernetes.io/uninitialized:NoScheduletaint, the validation pod stayedPendinguntil the 360s pod-wait timed out, failing the test with a misleadingFailedSchedulingerror rather than a real product/VHD issue.Investigated from build 176544987 (windows-2022-containerd-gen2): node
winw06a000000reported ready at 887.774s still carrying theuninitializedtaint; the validation pod then failed to schedule and the test timed out at 1250.370s.Change
Add a toleration for
node.cloudprovider.kubernetes.io/uninitialized:NoScheduletopodWindows()so the validation pod schedules onto the target node as soon as it's Ready, without waiting on CCM taint removal.Testing
go build ./...andgo vet ./...pass ine2e/.gofmt -lclean.