Skip to content
4 changes: 2 additions & 2 deletions kubectl-plugin/pkg/cmd/create/create_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util"
"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/generation"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

func TestRayCreateClusterComplete(t *testing.T) {
Expand Down Expand Up @@ -226,7 +226,7 @@ func TestRayClusterCreateClusterRun(t *testing.T) {
},
}

rayClient := rayClientFake.NewSimpleClientset(rayClusters...)
rayClient := clienttesting.NewRayClientset(rayClusters...)
k8sClients := client.NewClientForTesting(kubefake.NewClientset(), rayClient)

err := options.Run(context.Background(), k8sClients)
Expand Down
10 changes: 5 additions & 5 deletions kubectl-plugin/pkg/cmd/get/get_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

// This is to test Complete() and ensure that it is setting the namespace and cluster correctly
Expand Down Expand Up @@ -159,7 +159,7 @@ func TestRayClusterGetRun(t *testing.T) {
}

kubeClientSet := kubefake.NewClientset()
rayClient := rayClientFake.NewSimpleClientset(rayCluster)
rayClient := clienttesting.NewRayClientset(rayCluster)
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

// Initialize the printer with an empty print options since we are setting the column definition later
Expand Down Expand Up @@ -273,7 +273,7 @@ func TestGetRayClusters(t *testing.T) {
},
{
name: "should not error if a cluster name is provided, searching all namespaces, and clusters are found",
cluster: "my-cluster",
cluster: "raycluster-kuberay",
namespace: nil,
allFakeRayClusters: []runtime.Object{rayCluster},
expectedRayClusters: []runtime.Object{rayCluster},
Expand All @@ -286,7 +286,7 @@ func TestGetRayClusters(t *testing.T) {
},
{
name: "should not error if a cluster name is provided, searching one namespace, and clusters are found",
cluster: "my-cluster",
cluster: "raycluster-kuberay",
namespace: &namespace,
allFakeRayClusters: []runtime.Object{rayCluster},
expectedRayClusters: []runtime.Object{rayCluster},
Expand Down Expand Up @@ -318,7 +318,7 @@ func TestGetRayClusters(t *testing.T) {
}

kubeClientSet := kubefake.NewClientset()
rayClient := rayClientFake.NewSimpleClientset(tc.allFakeRayClusters...)
rayClient := clienttesting.NewRayClientset(tc.allFakeRayClusters...)
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

rayClusters, err := getRayClusters(context.Background(), &fakeClusterGetOptions, k8sClients)
Expand Down
4 changes: 2 additions & 2 deletions kubectl-plugin/pkg/cmd/get/get_nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util"
"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

func TestRayNodesGetComplete(t *testing.T) {
Expand Down Expand Up @@ -278,7 +278,7 @@ pod-2 1 1 1 1Gi cluster-1 worker group-2 120m
}

kubeClientSet := kubefake.NewClientset(tc.pods...)
rayClient := rayClientFake.NewSimpleClientset()
rayClient := clienttesting.NewRayClientset()
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

err := fakeGetNodesOptions.Run(context.Background(), k8sClients)
Expand Down
4 changes: 2 additions & 2 deletions kubectl-plugin/pkg/cmd/get/get_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

// Tests the Run() step of the command and ensure that the output is as expected.
Expand Down Expand Up @@ -47,7 +47,7 @@ func TestTokenGetRun(t *testing.T) {
}

kubeClientSet := kubefake.NewClientset(secret)
rayClient := rayClientFake.NewSimpleClientset(rayCluster)
rayClient := clienttesting.NewRayClientset(rayCluster)
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

cmd := &cobra.Command{}
Expand Down
6 changes: 3 additions & 3 deletions kubectl-plugin/pkg/cmd/get/get_workergroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import (

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util"
"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

func TestRayWorkerGroupGetComplete(t *testing.T) {
Expand Down Expand Up @@ -407,7 +407,7 @@ group-1 1 5 1/1 2 1 1 1Gi cluster-1
}

kubeClientSet := kubefake.NewClientset(tc.pods...)
rayClient := rayClientFake.NewSimpleClientset(tc.rayClusters...)
rayClient := clienttesting.NewRayClientset(tc.rayClusters...)
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

err := fakeGetWorkerGroupsOptions.Run(context.Background(), k8sClients)
Expand Down Expand Up @@ -679,7 +679,7 @@ func TestGetWorkerGroupDetails(t *testing.T) {
})
}

rayClient := rayClientFake.NewSimpleClientset()
rayClient := clienttesting.NewRayClientset()
k8sClients := client.NewClientForTesting(kubeClientSet, rayClient)

workerGroups, err := getWorkerGroupDetails(context.Background(), tc.enrichedWorkerGroupSpecs, k8sClients)
Expand Down
10 changes: 5 additions & 5 deletions kubectl-plugin/pkg/util/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func TestGetRayHeadSvcNameByRayCluster(t *testing.T) {
}

kubeClientSet := kubeFake.NewClientset(kubeObjects...)
rayClient := rayClientFake.NewSimpleClientset(rayObjects...)
rayClient := rayClientFake.NewClientset(rayObjects...)
client := NewClientForTesting(kubeClientSet, rayClient)

tests := []struct {
Expand Down Expand Up @@ -269,7 +269,7 @@ func TestGetRayHeadSvcNameByRayJob(t *testing.T) {
}

kubeClientSet := kubeFake.NewClientset(kubeObjects...)
rayClient := rayClientFake.NewSimpleClientset(rayObjects...)
rayClient := rayClientFake.NewClientset(rayObjects...)
client := NewClientForTesting(kubeClientSet, rayClient)

tests := []struct {
Expand Down Expand Up @@ -348,7 +348,7 @@ func TestGetRayHeadSvcNameByRayService(t *testing.T) {
}

kubeClientSet := kubeFake.NewClientset(kubeObjects...)
rayClient := rayClientFake.NewSimpleClientset(rayObjects...)
rayClient := rayClientFake.NewClientset(rayObjects...)
client := NewClientForTesting(kubeClientSet, rayClient)

tests := []struct {
Expand Down Expand Up @@ -436,8 +436,8 @@ func TestWaitRayClusterProvisioned(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
kubeClientSet := kubeFake.NewSimpleClientset()
rayClient := rayClientFake.NewSimpleClientset()
kubeClientSet := kubeFake.NewClientset()
rayClient := rayClientFake.NewClientset()

fakeWatcher := watch.NewFake()
go func() {
Expand Down
16 changes: 16 additions & 0 deletions kubectl-plugin/pkg/util/client/testing/clientset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package testing

import (
"k8s.io/apimachinery/pkg/runtime"

rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

// NewRayClientset creates a fake Ray clientset with FieldSelector support.
// This is a convenience wrapper that creates a clientset and adds the
// FieldSelector reactor, so tests don't need to set it up manually.
func NewRayClientset(objects ...runtime.Object) *rayClientFake.Clientset {
client := rayClientFake.NewClientset(objects...)
AddRayClusterListFieldSelectorReactor(client)
return client
}
61 changes: 61 additions & 0 deletions kubectl-plugin/pkg/util/client/testing/reactor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package testing

import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"

rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

// AddRayClusterFieldSelectorReactor adds a reactor to the fake Ray client that
// simulates server-side FieldSelector filtering for RayCluster List operations.
// This allows tests to verify FieldSelector behavior without manual name checks.
func AddRayClusterListFieldSelectorReactor(client *rayClientFake.Clientset) {
client.PrependReactor("list", "rayclusters", func(action kubetesting.Action) (bool, runtime.Object, error) {
listAction, ok := action.(kubetesting.ListAction)
if !ok {
return false, nil, nil
}

fieldSelector := listAction.GetListRestrictions().Fields
if fieldSelector == nil || fieldSelector.Empty() {
return false, nil, nil
}

// Get all clusters first using the tracker
namespace := listAction.GetNamespace()
allClusters, err := client.Tracker().List(
rayv1.SchemeGroupVersion.WithResource("rayclusters"),
rayv1.SchemeGroupVersion.WithKind("RayCluster"),
namespace,
)
if err != nil {
return true, nil, err
}

clusterList, ok := allClusters.(*rayv1.RayClusterList)
if !ok {
return false, nil, nil
}

// Filter based on field selector
filtered := &rayv1.RayClusterList{}
for _, cluster := range clusterList.Items {
if fieldSelector.Matches(clusterFieldSet(&cluster)) {
filtered.Items = append(filtered.Items, cluster)
}
}

return true, filtered, nil
})
}

// clusterFieldSet returns a fields.Set for a RayCluster that can be matched against a FieldSelector.
func clusterFieldSet(cluster *rayv1.RayCluster) fields.Set {
return fields.Set{
"metadata.name": cluster.Name,
"metadata.namespace": cluster.Namespace,
}
}
93 changes: 93 additions & 0 deletions kubectl-plugin/pkg/util/client/testing/reactor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package testing

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)

func TestAddRayClusterListFieldSelectorReactor(t *testing.T) {
cluster1 := &rayv1.RayCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-1",
Namespace: "default",
},
}
cluster2 := &rayv1.RayCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-2",
Namespace: "default",
},
}
cluster3 := &rayv1.RayCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-3",
Namespace: "other",
},
}

tests := []struct {
name string
namespace string
fieldSelector string
expectedNames []string
expectedCount int
}{
{
name: "no field selector returns all clusters",
namespace: "default",
fieldSelector: "",
expectedCount: 2,
expectedNames: []string{"cluster-1", "cluster-2"},
},
{
name: "field selector filters by name",
namespace: "default",
fieldSelector: "metadata.name=cluster-1",
expectedCount: 1,
expectedNames: []string{"cluster-1"},
},
{
name: "field selector with non-existent name returns empty",
namespace: "default",
fieldSelector: "metadata.name=non-existent",
expectedCount: 0,
expectedNames: []string{},
},
{
name: "namespace filtering returns only clusters in specified namespace",
namespace: "other",
fieldSelector: "",
expectedCount: 1,
expectedNames: []string{"cluster-3"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
rayClient := rayClientFake.NewClientset(cluster1, cluster2, cluster3)
AddRayClusterListFieldSelectorReactor(rayClient)

listOpts := metav1.ListOptions{}
if tc.fieldSelector != "" {
listOpts.FieldSelector = tc.fieldSelector
}

result, err := rayClient.RayV1().RayClusters(tc.namespace).List(context.Background(), listOpts)
require.NoError(t, err)
assert.Len(t, result.Items, tc.expectedCount)

names := make([]string, len(result.Items))
for i, cluster := range result.Items {
names[i] = cluster.Name
}
assert.ElementsMatch(t, tc.expectedNames, names)
})
}
}
3 changes: 0 additions & 3 deletions kubectl-plugin/pkg/util/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ func workerGroupCompletionFunc(cmd *cobra.Command, args []string, toComplete str

seen := make(map[string]bool)
for _, rayCluster := range rayClusterList.Items {
if cluster != "" && rayCluster.Name != cluster {
continue
}
Comment on lines -112 to -114
Copy link
Member

Choose a reason for hiding this comment

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

why delete this line??

cc @cheyu @lorriexingfang @Jiekai for review

Copy link
Member

Choose a reason for hiding this comment

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

ah since we use field selector now

for _, spec := range rayCluster.Spec.WorkerGroupSpecs {
if !seen[spec.GroupName] && (toComplete == "" || strings.HasPrefix(spec.GroupName, toComplete)) {
comps = append(comps, spec.GroupName)
Expand Down
3 changes: 2 additions & 1 deletion kubectl-plugin/pkg/util/completion/completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/ray-project/kuberay/kubectl-plugin/pkg/util"
"github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client"
clienttesting "github.com/ray-project/kuberay/kubectl-plugin/pkg/util/client/testing"
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
rayClientFake "github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/fake"
)
Expand Down Expand Up @@ -335,7 +336,7 @@ func TestWorkerGroupCompletionFunc(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
kubeClientSet := kubefake.NewClientset()
rayClient := rayClientFake.NewClientset(tc.rayClusters...)
rayClient := clienttesting.NewRayClientset(tc.rayClusters...)
k8sClient := client.NewClientForTesting(kubeClientSet, rayClient)

cmd := &cobra.Command{}
Expand Down
Loading