Skip to content

Commit 44c545f

Browse files
committed
add testing & support feature-gate in helm chart
1 parent c90a5ef commit 44c545f

File tree

6 files changed

+135
-1
lines changed

6 files changed

+135
-1
lines changed

helm-chart/kuberay-operator/templates/_helpers.tpl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,18 @@ Create the name of the service account to use
5454
{{ default "default" .Values.serviceAccount.name }}
5555
{{- end -}}
5656
{{- end -}}
57+
58+
59+
{{/*
60+
FeatureGates
61+
*/}}
62+
{{- define "kuberay.featureGates" -}}
63+
{{- $features := "" }}
64+
{{- range .Values.featureGates }}
65+
{{- $str := printf "%s=%t," .name .enabled }}
66+
{{- $features = print $features $str }}
67+
{{- end }}
68+
{{- with .Values.featureGates }}
69+
--feature-gates={{ $features | trimSuffix "," }}
70+
{{- end }}
71+
{{- end }}

helm-chart/kuberay-operator/templates/deployment.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ spec:
5656
- /manager
5757
args:
5858
{{- $argList := list -}}
59+
{{- $argList = append $argList (include "kuberay.featureGates" . | trim) -}}
5960
{{- if .Values.batchScheduler.enabled -}}
6061
{{- $argList = append $argList "--enable-batch-scheduler" -}}
6162
{{- end -}}

helm-chart/kuberay-operator/values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ readinessProbe:
5858
batchScheduler:
5959
enabled: false
6060

61+
featureGates:
62+
- name: RayClusterStatusConditions
63+
enabled: false
64+
65+
6166
# Set up `securityContext` to improve Pod security.
6267
# See https://github.com/ray-project/kuberay/blob/master/docs/guidance/pod-security.md for further guidance.
6368
podSecurityContext: {}

ray-operator/controllers/ray/raycluster_controller_unit_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/ray-project/kuberay/ray-operator/controllers/ray/common"
2626
"github.com/ray-project/kuberay/ray-operator/controllers/ray/utils"
2727
"github.com/ray-project/kuberay/ray-operator/pkg/client/clientset/versioned/scheme"
28+
"github.com/ray-project/kuberay/ray-operator/pkg/features"
2829

2930
. "github.com/onsi/ginkgo/v2"
3031
"github.com/stretchr/testify/assert"
@@ -33,6 +34,7 @@ import (
3334
corev1 "k8s.io/api/core/v1"
3435
rbacv1 "k8s.io/api/rbac/v1"
3536
k8serrors "k8s.io/apimachinery/pkg/api/errors"
37+
"k8s.io/apimachinery/pkg/api/meta"
3638
"k8s.io/apimachinery/pkg/api/resource"
3739
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3840
"k8s.io/apimachinery/pkg/labels"
@@ -1664,6 +1666,12 @@ func TestCalculateStatus(t *testing.T) {
16641666
Status: corev1.PodStatus{
16651667
PodIP: headNodeIP,
16661668
Phase: corev1.PodRunning,
1669+
Conditions: []corev1.PodCondition{
1670+
{
1671+
Type: corev1.PodReady,
1672+
Status: corev1.ConditionTrue,
1673+
},
1674+
},
16671675
},
16681676
}
16691677
runtimeObjects := []runtime.Object{headPod, headService}
@@ -1687,6 +1695,47 @@ func TestCalculateStatus(t *testing.T) {
16871695
assert.Equal(t, headService.Name, newInstance.Status.Head.ServiceName)
16881696
assert.NotNil(t, newInstance.Status.StateTransitionTimes, "Cluster state transition timestamp should be created")
16891697
assert.Equal(t, newInstance.Status.LastUpdateTime, newInstance.Status.StateTransitionTimes[rayv1.Ready])
1698+
1699+
// Test empty conditions with feature gate disabled
1700+
newInstance, _ = r.calculateStatus(ctx, testRayCluster, nil)
1701+
assert.Empty(t, newInstance.Status.Conditions)
1702+
1703+
// enable feature gate for the following tests
1704+
defer features.SetFeatureGateDuringTest(t, features.RayClusterStatusConditions, true)()
1705+
1706+
// Test CheckRayHeadRunningAndReady with head pod running and ready
1707+
newInstance, _ = r.calculateStatus(ctx, testRayCluster, nil)
1708+
assert.True(t, meta.IsStatusConditionPresentAndEqual(newInstance.Status.Conditions, string(rayv1.HeadReady), metav1.ConditionTrue))
1709+
condition := meta.FindStatusCondition(newInstance.Status.Conditions, string(rayv1.HeadReady))
1710+
assert.Equal(t, "HeadPodRunningAndReady", condition.Reason)
1711+
assert.Equal(t, "Head pod is running and ready", condition.Message)
1712+
1713+
// Test CheckRayHeadRunningAndReady with head pod not ready
1714+
headPod.Status.Conditions = []corev1.PodCondition{
1715+
{
1716+
Type: corev1.PodReady,
1717+
Status: corev1.ConditionFalse,
1718+
},
1719+
}
1720+
runtimeObjects = []runtime.Object{headPod, headService}
1721+
fakeClient = clientFake.NewClientBuilder().WithScheme(newScheme).WithRuntimeObjects(runtimeObjects...).Build()
1722+
r.Client = fakeClient
1723+
newInstance, _ = r.calculateStatus(ctx, testRayCluster, nil)
1724+
assert.True(t, meta.IsStatusConditionPresentAndEqual(newInstance.Status.Conditions, string(rayv1.HeadReady), metav1.ConditionFalse))
1725+
condition = meta.FindStatusCondition(newInstance.Status.Conditions, string(rayv1.HeadReady))
1726+
assert.Equal(t, "HeadPodNotReady", condition.Reason)
1727+
assert.Equal(t, "Head pod is not ready", condition.Message)
1728+
1729+
// Test CheckRayHeadRunningAndReady with head pod not running
1730+
headPod.Status.Phase = corev1.PodFailed
1731+
runtimeObjects = []runtime.Object{headPod, headService}
1732+
fakeClient = clientFake.NewClientBuilder().WithScheme(newScheme).WithRuntimeObjects(runtimeObjects...).Build()
1733+
r.Client = fakeClient
1734+
newInstance, _ = r.calculateStatus(ctx, testRayCluster, nil)
1735+
assert.True(t, meta.IsStatusConditionPresentAndEqual(newInstance.Status.Conditions, string(rayv1.HeadReady), metav1.ConditionFalse))
1736+
condition = meta.FindStatusCondition(newInstance.Status.Conditions, string(rayv1.HeadReady))
1737+
assert.Equal(t, "HeadPodNotRunning", condition.Reason)
1738+
assert.Equal(t, "Head pod is not running", condition.Message)
16901739
}
16911740

16921741
func TestStateTransitionTimes_NoStateChange(t *testing.T) {

ray-operator/controllers/ray/utils/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func CheckRayHeadRunningAndReady(ctx context.Context, pods corev1.PodList) (bool
9494
log.Info("No head pod found in the pod list")
9595
return false, "HeadPodNotFound", "No head pod found in the pod list"
9696
}
97-
log.Info("Head pod is running and ready !!!")
97+
log.Info("Head pod is running and ready")
9898
return true, "HeadPodRunningAndReady", "Head pod is running and ready"
9999
}
100100

ray-operator/controllers/ray/utils/util_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,31 @@ func createSomePodWithCondition(typ corev1.PodConditionType, status corev1.Condi
201201
}
202202
}
203203

204+
func createRayHeadPodWithPhaseAndCOndition(phase corev1.PodPhase, typ corev1.PodConditionType, status corev1.ConditionStatus) (pod *corev1.Pod) {
205+
return &corev1.Pod{
206+
TypeMeta: metav1.TypeMeta{
207+
APIVersion: "v1",
208+
Kind: "Pod",
209+
},
210+
ObjectMeta: metav1.ObjectMeta{
211+
Name: "raycluster-sample-head",
212+
Namespace: "default",
213+
Labels: map[string]string{
214+
"ray.io/node-type": string(rayv1.HeadNode),
215+
},
216+
},
217+
Status: corev1.PodStatus{
218+
Phase: phase,
219+
Conditions: []corev1.PodCondition{
220+
{
221+
Type: typ,
222+
Status: status,
223+
},
224+
},
225+
},
226+
}
227+
}
228+
204229
func TestGetHeadGroupServiceAccountName(t *testing.T) {
205230
tests := map[string]struct {
206231
input *rayv1.RayCluster
@@ -523,3 +548,42 @@ env_vars:
523548
})
524549
}
525550
}
551+
552+
func TestCheckRayHeadRunningAndReady(t *testing.T) {
553+
tests := map[string]struct {
554+
pods corev1.PodList
555+
expected bool
556+
}{
557+
"should return true if Ray head pod is running and ready": {
558+
pods: corev1.PodList{
559+
Items: []corev1.Pod{
560+
*createRayHeadPodWithPhaseAndCOndition(corev1.PodRunning, corev1.PodReady, corev1.ConditionTrue),
561+
},
562+
},
563+
expected: true,
564+
},
565+
"should return false if Ray head pod is not running": {
566+
pods: corev1.PodList{
567+
Items: []corev1.Pod{
568+
*createRayHeadPodWithPhaseAndCOndition(corev1.PodPending, corev1.PodReady, corev1.ConditionFalse),
569+
},
570+
},
571+
expected: false,
572+
},
573+
"should return false if Ray head pod is not ready": {
574+
pods: corev1.PodList{
575+
Items: []corev1.Pod{
576+
*createRayHeadPodWithPhaseAndCOndition(corev1.PodRunning, corev1.PodReady, corev1.ConditionFalse),
577+
},
578+
},
579+
expected: false,
580+
},
581+
}
582+
583+
for name, tc := range tests {
584+
t.Run(name, func(t *testing.T) {
585+
isRunning, _, _ := CheckRayHeadRunningAndReady(context.Background(), tc.pods)
586+
assert.Equal(t, tc.expected, isRunning)
587+
})
588+
}
589+
}

0 commit comments

Comments
 (0)