[cherry-pick release-1.18] Fast-fail backup when built-in data mover has no running node-agent (#10360)
Run the E2E test on kind / setup-test-matrix (push) Successful in 2s
e2e-test-kind.yaml / extract (push) Successful in 16s
Run the E2E test on kind / get-go-version (push) Successful in 18s
push.yml / extract (push) Successful in 12s
Main CI / get-go-version (push) Successful in 13s
Run the E2E test on kind / build (push) Failing after 25s
Run the E2E test on kind / run-e2e-test (push) Skipped
Main CI / Build (push) Failing after 34s

* Fast-fail backup when built-in data mover has no running node-agent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Joseph <jvaikath@redhat.com>
Signed-off-by: Chai Bot <ship-help-github@redhat.com>

* Run make update to fix import ordering

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Joseph <jvaikath@redhat.com>
Signed-off-by: Chai Bot <ship-help-github@redhat.com>

* Create new changelog for release 10360

Signed-off-by: Chai Bot <ship-help-github@redhat.com>

---------

Signed-off-by: Joseph <jvaikath@redhat.com>
Signed-off-by: Chai Bot <ship-help-github@redhat.com>
Co-authored-by: Joseph <jvaikath@redhat.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>
This commit is contained in:
Chai-bot
2026-08-25 22:52:02 +00:00
committed by GitHub
co-authored by Claude Opus 4.6 Joseph Tiger Kaovilai
parent 882d409195
commit 7770a0884e
5 changed files with 228 additions and 12 deletions
+30
View File
@@ -22,6 +22,8 @@ import (
"fmt"
"github.com/cockroachdb/errors"
"github.com/sirupsen/logrus"
appsv1api "k8s.io/api/apps/v1"
corev1api "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -80,6 +82,34 @@ func KbClientIsRunningInNode(ctx context.Context, namespace string, nodeName str
return isRunningInNode(ctx, namespace, nodeName, nil, kubeClient)
}
// IsReady checks whether the node-agent daemonset has at least one ready pod
// by inspecting the DaemonSet status. It only checks the daemonset for node
// OS types that are present in the cluster, following the same pattern as
// server.checkNodeAgent.
func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client, log logrus.FieldLogger) error {
if kube.WithLinuxNode(ctx, crClient, log) {
ds := new(appsv1api.DaemonSet)
if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonSet}, ds); err != nil {
return errors.Wrap(err, "failed to get linux node-agent daemonset")
}
if ds.Status.NumberReady > 0 {
return nil
}
}
if kube.WithWindowsNode(ctx, crClient, log) {
ds := new(appsv1api.DaemonSet)
if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonsetWindows}, ds); err != nil {
return errors.Wrap(err, "failed to get windows node-agent daemonset")
}
if ds.Status.NumberReady > 0 {
return nil
}
}
return errors.New("node-agent is not ready: no ready pods found")
}
// IsRunningInNode checks if the node agent pod is running properly in a specified node through controller client. If not, return the error found
func IsRunningInNode(ctx context.Context, namespace string, nodeName string, crClient ctrlclient.Client) error {
return isRunningInNode(ctx, namespace, nodeName, crClient, nil)
+141
View File
@@ -20,6 +20,7 @@ import (
"testing"
"github.com/cockroachdb/errors"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1api "k8s.io/api/apps/v1"
@@ -213,6 +214,146 @@ func TestIsRunningInNode(t *testing.T) {
}
}
func TestIsReady(t *testing.T) {
scheme := runtime.NewScheme()
appsv1api.AddToScheme(scheme)
corev1api.AddToScheme(scheme)
log := logrus.New()
linuxNode := &corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "linux-node",
Labels: map[string]string{kube.NodeOSLabel: kube.NodeOSLinux},
},
}
windowsNode := &corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "windows-node",
Labels: map[string]string{kube.NodeOSLabel: kube.NodeOSWindows},
},
}
dsLinuxNotReady := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent"},
Status: appsv1api.DaemonSetStatus{NumberReady: 0},
}
dsLinuxReady := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent"},
Status: appsv1api.DaemonSetStatus{NumberReady: 3},
}
dsWindowsNotReady := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent-windows"},
Status: appsv1api.DaemonSetStatus{NumberReady: 0},
}
dsWindowsReady := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent-windows"},
Status: appsv1api.DaemonSetStatus{NumberReady: 2},
}
tests := []struct {
name string
kubeClientObj []runtime.Object
namespace string
expectErr string
}{
{
name: "no nodes in cluster",
namespace: "fake-ns",
expectErr: "node-agent is not ready: no ready pods found",
},
{
name: "linux node exists but daemonset not found",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
},
expectErr: "failed to get linux node-agent daemonset",
},
{
name: "linux node and daemonset exist but no ready pods",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
dsLinuxNotReady,
},
expectErr: "node-agent is not ready: no ready pods found",
},
{
name: "linux node and daemonset with ready pods",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
dsLinuxReady,
},
},
{
name: "windows node and daemonset with ready pods",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
windowsNode,
dsWindowsReady,
},
},
{
name: "windows node and daemonset with no ready pods",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
windowsNode,
dsWindowsNotReady,
},
expectErr: "node-agent is not ready: no ready pods found",
},
{
name: "both node types with both daemonsets ready",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
windowsNode,
dsLinuxReady,
dsWindowsReady,
},
},
{
name: "both node types but neither daemonset has ready pods",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
windowsNode,
dsLinuxNotReady,
dsWindowsNotReady,
},
expectErr: "node-agent is not ready: no ready pods found",
},
{
name: "linux not ready but windows ready",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
linuxNode,
windowsNode,
dsLinuxNotReady,
dsWindowsReady,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fakeClient := clientFake.NewClientBuilder().
WithScheme(scheme).
WithRuntimeObjects(test.kubeClientObj...).
Build()
err := IsReady(t.Context(), test.namespace, fakeClient, log)
if test.expectErr == "" {
assert.NoError(t, err)
} else {
assert.ErrorContains(t, err, test.expectErr)
}
})
}
}
func TestGetPodSpec(t *testing.T) {
podSpec := corev1api.PodSpec{
NodeName: "fake-node",