From ef5375e3e39327e96308477addca92c6bc6f4121 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:19:55 +0000 Subject: [PATCH 1/4] Check both daemonsets before returning non-NotFound lookup error in IsReady Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> --- pkg/nodeagent/node_agent.go | 16 ++++++++--- pkg/nodeagent/node_agent_test.go | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index b449a91f4..755a56d85 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -82,13 +82,17 @@ func KbClientIsRunningInNode(ctx context.Context, namespace string, nodeName str } // IsReady checks whether the node-agent daemonset has at least one ready pod -// by inspecting the DaemonSet status. +// by inspecting the DaemonSet status. Both the linux and windows daemonsets +// are checked before returning any non-NotFound lookup error, so that a +// transient error fetching one daemonset does not mask the other daemonset +// being ready. func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client) error { dsLinux := new(appsv1api.DaemonSet) + var lookupErr error if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonSet}, dsLinux); err != nil { dsLinux = nil if !apierrors.IsNotFound(err) { - return errors.Wrap(err, "failed to get linux node-agent daemonset") + lookupErr = errors.Wrap(err, "failed to get linux node-agent daemonset") } } @@ -96,7 +100,9 @@ func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client) if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonsetWindows}, dsWindows); err != nil { dsWindows = nil if !apierrors.IsNotFound(err) { - return errors.Wrap(err, "failed to get windows node-agent daemonset") + if lookupErr == nil { + lookupErr = errors.Wrap(err, "failed to get windows node-agent daemonset") + } } } @@ -108,6 +114,10 @@ func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client) return nil } + if lookupErr != nil { + return lookupErr + } + return errors.New("node-agent is not ready: no ready pods found") } diff --git a/pkg/nodeagent/node_agent_test.go b/pkg/nodeagent/node_agent_test.go index 9bba67ec4..24e1471d9 100644 --- a/pkg/nodeagent/node_agent_test.go +++ b/pkg/nodeagent/node_agent_test.go @@ -275,6 +275,52 @@ func TestIsReady(t *testing.T) { }, expectErr: "failed to get windows node-agent daemonset: fake-get-error", }, + { + name: "linux daemonset get error but windows ready", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + dsWindowsReady, + }, + interceptor: &interceptor.Funcs{ + Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error { + if key.Name == "node-agent" { + return errors.New("fake-get-error") + } + return c.Get(ctx, key, obj, opts...) + }, + }, + }, + { + name: "windows daemonset get error but linux ready", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + dsLinuxReady, + }, + interceptor: &interceptor.Funcs{ + Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error { + if key.Name == "node-agent-windows" { + return errors.New("fake-get-error") + } + return c.Get(ctx, key, obj, opts...) + }, + }, + }, + { + name: "linux daemonset get error and windows not ready", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + dsWindowsNotReady, + }, + interceptor: &interceptor.Funcs{ + Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error { + if key.Name == "node-agent" { + return errors.New("fake-get-error") + } + return c.Get(ctx, key, obj, opts...) + }, + }, + expectErr: "failed to get linux node-agent daemonset: fake-get-error", + }, { name: "linux ds exist but no ready pods", namespace: "fake-ns", From da01c1ca95b1d388e699f6476d4c2c462b1717cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:27:05 +0000 Subject: [PATCH 2/4] Combine both daemonset lookup errors (windows retained as secondary) Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> --- pkg/nodeagent/node_agent.go | 4 +--- pkg/nodeagent/node_agent_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index 755a56d85..6dcdbf89b 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -100,9 +100,7 @@ func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client) if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonsetWindows}, dsWindows); err != nil { dsWindows = nil if !apierrors.IsNotFound(err) { - if lookupErr == nil { - lookupErr = errors.Wrap(err, "failed to get windows node-agent daemonset") - } + lookupErr = errors.CombineErrors(lookupErr, errors.Wrap(err, "failed to get windows node-agent daemonset")) } } diff --git a/pkg/nodeagent/node_agent_test.go b/pkg/nodeagent/node_agent_test.go index 24e1471d9..40e0d3fd8 100644 --- a/pkg/nodeagent/node_agent_test.go +++ b/pkg/nodeagent/node_agent_test.go @@ -18,6 +18,7 @@ package nodeagent import ( "context" + "fmt" "testing" "github.com/cockroachdb/errors" @@ -408,6 +409,35 @@ func TestIsReady(t *testing.T) { } } +// TestIsReadyBothDaemonsetsGetError ensures that when both daemonset lookups +// return a non-NotFound error, the linux error is returned as the primary +// error while the windows error is retained as a secondary/attached error +// rather than being silently discarded. +func TestIsReadyBothDaemonsetsGetError(t *testing.T) { + scheme := runtime.NewScheme() + appsv1api.AddToScheme(scheme) + + fakeClient := clientFake.NewClientBuilder(). + WithScheme(scheme). + WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error { + if key.Name == "node-agent" { + return errors.New("fake-linux-get-error") + } + if key.Name == "node-agent-windows" { + return errors.New("fake-windows-get-error") + } + return c.Get(ctx, key, obj, opts...) + }, + }). + Build() + + err := IsReady(t.Context(), "fake-ns", fakeClient) + require.Error(t, err) + assert.EqualError(t, err, "failed to get linux node-agent daemonset: fake-linux-get-error") + assert.Contains(t, fmt.Sprintf("%+v", err), "failed to get windows node-agent daemonset: fake-windows-get-error") +} + func TestGetPodSpec(t *testing.T) { podSpec := corev1api.PodSpec{ NodeName: "fake-node", From b4bcea9236840020715b120a60b640134078c814 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:27:40 +0000 Subject: [PATCH 3/4] Add changelog entry for PR #10403 Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> --- changelogs/unreleased/10403-kaovilai | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/10403-kaovilai diff --git a/changelogs/unreleased/10403-kaovilai b/changelogs/unreleased/10403-kaovilai new file mode 100644 index 000000000..e87620ce6 --- /dev/null +++ b/changelogs/unreleased/10403-kaovilai @@ -0,0 +1 @@ +Check both node-agent daemonsets before returning a non-NotFound lookup error in IsReady, so a transient error fetching one daemonset no longer masks the other daemonset being ready \ No newline at end of file From acefa77d89b9e92b71b52fda343911424057f4e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:45:53 +0000 Subject: [PATCH 4/4] Fix testifylint require-error lint failure in node_agent_test.go Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> --- pkg/nodeagent/node_agent_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/nodeagent/node_agent_test.go b/pkg/nodeagent/node_agent_test.go index 40e0d3fd8..4a406dd39 100644 --- a/pkg/nodeagent/node_agent_test.go +++ b/pkg/nodeagent/node_agent_test.go @@ -433,8 +433,7 @@ func TestIsReadyBothDaemonsetsGetError(t *testing.T) { Build() err := IsReady(t.Context(), "fake-ns", fakeClient) - require.Error(t, err) - assert.EqualError(t, err, "failed to get linux node-agent daemonset: fake-linux-get-error") + require.EqualError(t, err, "failed to get linux node-agent daemonset: fake-linux-get-error") assert.Contains(t, fmt.Sprintf("%+v", err), "failed to get windows node-agent daemonset: fake-windows-get-error") }