Skip to content

Commit 81e8f81

Browse files
astefanuttiopenshift-merge-robot
authored andcommitted
e2e: Use Ray dashboard API to retrieve job logs
1 parent cd8b115 commit 81e8f81

File tree

6 files changed

+164
-28
lines changed

6 files changed

+164
-28
lines changed

.github/actions/kind/action.yml

+9
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,12 @@ runs:
4747
echo "KinD cluster:"
4848
kubectl cluster-info
4949
kubectl describe nodes
50+
51+
- name: Install Ingress controller
52+
shell: bash
53+
run: |
54+
VERSION=controller-v1.6.4
55+
echo "Deploying Ingress controller into KinD cluster"
56+
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/"${VERSION}"/deploy/static/provider/kind/deploy.yaml | sed "s/--publish-status-address=localhost/--report-node-internal-ip-address\\n - --status-update-interval=10/g" | kubectl apply -f -
57+
kubectl annotate ingressclass nginx "ingressclass.kubernetes.io/is-default-class=true"
58+
kubectl -n ingress-nginx wait --timeout=300s --for=condition=Available deployments --all

test/e2e/mnist_rayjob_mcad_raycluster_test.go

+102-1
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@ package e2e
1818

1919
import (
2020
"encoding/base64"
21+
"net/url"
2122
"testing"
2223

2324
. "github.com/onsi/gomega"
2425
mcadv1beta1 "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/apis/controller/v1beta1"
2526
rayv1alpha1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1alpha1"
2627

2728
corev1 "k8s.io/api/core/v1"
29+
networkingv1 "k8s.io/api/networking/v1"
2830
"k8s.io/apimachinery/pkg/api/resource"
2931
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/util/intstr"
33+
34+
routev1 "github.com/openshift/api/route/v1"
3035

3136
. "github.com/project-codeflare/codeflare-operator/test/support"
3237
)
@@ -252,8 +257,104 @@ func TestMNISTRayJobMCADRayCluster(t *testing.T) {
252257
test.Expect(err).NotTo(HaveOccurred())
253258
test.T().Logf("Created RayJob %s/%s successfully", rayJob.Namespace, rayJob.Name)
254259

260+
var rayDashboardURL url.URL
261+
if IsOpenShift(test) {
262+
// Create a route to expose the Ray cluster API
263+
route := &routev1.Route{
264+
TypeMeta: metav1.TypeMeta{
265+
APIVersion: routev1.GroupVersion.String(),
266+
Kind: "Route",
267+
},
268+
ObjectMeta: metav1.ObjectMeta{
269+
Namespace: namespace.Name,
270+
Name: "ray-dashboard",
271+
},
272+
Spec: routev1.RouteSpec{
273+
To: routev1.RouteTargetReference{
274+
Name: "raycluster-head-svc",
275+
},
276+
Port: &routev1.RoutePort{
277+
TargetPort: intstr.FromString("dashboard"),
278+
},
279+
},
280+
}
281+
282+
_, err := test.Client().Route().RouteV1().Routes(namespace.Name).Create(test.Ctx(), route, metav1.CreateOptions{})
283+
test.Expect(err).NotTo(HaveOccurred())
284+
test.T().Logf("Created Route %s/%s successfully", route.Namespace, route.Name)
285+
286+
test.T().Logf("Waiting for Route %s/%s to be admitted", route.Namespace, route.Name)
287+
test.Eventually(Route(test, route.Namespace, route.Name), TestTimeoutMedium).
288+
Should(WithTransform(ConditionStatus(routev1.RouteAdmitted), Equal(corev1.ConditionTrue)))
289+
290+
route = GetRoute(test, route.Namespace, route.Name)
291+
292+
rayDashboardURL = url.URL{
293+
Scheme: "http",
294+
Host: route.Status.Ingress[0].Host,
295+
}
296+
} else {
297+
ingress := &networkingv1.Ingress{
298+
TypeMeta: metav1.TypeMeta{
299+
APIVersion: networkingv1.SchemeGroupVersion.String(),
300+
Kind: "Ingress",
301+
},
302+
ObjectMeta: metav1.ObjectMeta{
303+
Namespace: namespace.Name,
304+
Name: "ray-dashboard",
305+
Annotations: map[string]string{
306+
"nginx.ingress.kubernetes.io/use-regex": "true",
307+
"nginx.ingress.kubernetes.io/rewrite-target": "/$2",
308+
},
309+
},
310+
Spec: networkingv1.IngressSpec{
311+
Rules: []networkingv1.IngressRule{
312+
{
313+
IngressRuleValue: networkingv1.IngressRuleValue{
314+
HTTP: &networkingv1.HTTPIngressRuleValue{
315+
Paths: []networkingv1.HTTPIngressPath{
316+
{
317+
Path: "/ray-dashboard(/|$)(.*)",
318+
PathType: Ptr(networkingv1.PathTypePrefix),
319+
Backend: networkingv1.IngressBackend{
320+
Service: &networkingv1.IngressServiceBackend{
321+
Name: "raycluster-head-svc",
322+
Port: networkingv1.ServiceBackendPort{
323+
Name: "dashboard",
324+
},
325+
},
326+
},
327+
},
328+
},
329+
},
330+
},
331+
},
332+
},
333+
},
334+
}
335+
336+
_, err := test.Client().Core().NetworkingV1().Ingresses(ingress.Namespace).Create(test.Ctx(), ingress, metav1.CreateOptions{})
337+
test.Expect(err).NotTo(HaveOccurred())
338+
test.T().Logf("Created Ingress %s/%s successfully", ingress.Namespace, ingress.Name)
339+
340+
test.T().Logf("Waiting for Ingress %s/%s to be admitted", ingress.Namespace, ingress.Name)
341+
test.Eventually(Ingress(test, ingress.Namespace, ingress.Name), TestTimeoutMedium).
342+
Should(WithTransform(LoadBalancerIngresses, HaveLen(1)))
343+
344+
ingress = GetIngress(test, ingress.Namespace, ingress.Name)
345+
346+
rayDashboardURL = url.URL{
347+
Scheme: "http",
348+
Host: ingress.Status.LoadBalancer.Ingress[0].IP,
349+
Path: "ray-dashboard",
350+
}
351+
}
352+
353+
test.T().Logf("Connecting to Ray cluster at: %s", rayDashboardURL.String())
354+
rayClient := NewRayClusterClient(rayDashboardURL)
355+
255356
// Retrieving the job logs once it has completed or timed out
256-
defer WriteRayJobLogs(test, rayJob.Namespace, rayJob.Name)
357+
defer WriteRayJobAPILogs(test, rayClient, GetRayJobId(test, rayJob.Namespace, rayJob.Name))
257358

258359
test.T().Logf("Waiting for RayJob %s/%s to complete", rayJob.Namespace, rayJob.Name)
259360
test.Eventually(RayJob(test, rayJob.Namespace, rayJob.Name), TestTimeoutLong).

test/support/ingress.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright 2023.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package support
18+
19+
import (
20+
"github.com/onsi/gomega"
21+
22+
networkingv1 "k8s.io/api/networking/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
func Ingress(t Test, namespace, name string) func(g gomega.Gomega) *networkingv1.Ingress {
27+
return func(g gomega.Gomega) *networkingv1.Ingress {
28+
ingress, err := t.Client().Core().NetworkingV1().Ingresses(namespace).Get(t.Ctx(), name, metav1.GetOptions{})
29+
g.Expect(err).NotTo(gomega.HaveOccurred())
30+
return ingress
31+
}
32+
}
33+
34+
func GetIngress(t Test, namespace, name string) *networkingv1.Ingress {
35+
t.T().Helper()
36+
return Ingress(t, namespace, name)(t)
37+
}
38+
39+
func LoadBalancerIngresses(ingress *networkingv1.Ingress) []networkingv1.IngressLoadBalancerIngress {
40+
return ingress.Status.LoadBalancer.Ingress
41+
}

test/support/ray.go

+3-23
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package support
1818

1919
import (
20-
"encoding/json"
21-
2220
"github.com/onsi/gomega"
2321
rayv1alpha1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1alpha1"
2422

@@ -44,28 +42,10 @@ func RayJobStatus(job *rayv1alpha1.RayJob) rayv1alpha1.JobStatus {
4442
return job.Status.JobStatus
4543
}
4644

47-
func GetRayJobLogs(t Test, namespace, name string) []byte {
45+
func GetRayJobId(t Test, namespace, name string) string {
4846
t.T().Helper()
49-
50-
job := GetRayJob(t, namespace, name)
51-
52-
response := t.Client().Core().CoreV1().RESTClient().
53-
Get().
54-
AbsPath("/api/v1/namespaces", job.Namespace, "services", "http:"+job.Status.RayClusterName+"-head-svc:dashboard", "proxy", "api", "jobs", job.Status.JobId, "logs").
55-
Do(t.Ctx())
56-
t.Expect(response.Error()).NotTo(gomega.HaveOccurred())
57-
58-
body := map[string]string{}
59-
bytes, _ := response.Raw()
60-
t.Expect(json.Unmarshal(bytes, &body)).To(gomega.Succeed())
61-
t.Expect(body).To(gomega.HaveKey("logs"))
62-
63-
return []byte(body["logs"])
64-
}
65-
66-
func WriteRayJobLogs(t Test, namespace, name string) {
67-
t.T().Logf("Retrieving RayJob %s/%s logs", namespace, name)
68-
WriteToOutputDir(t, name, Log, GetRayJobLogs(t, namespace, name))
47+
job := RayJob(t, namespace, name)(t)
48+
return job.Status.JobId
6949
}
7050

7151
func RayCluster(t Test, namespace, name string) func(g gomega.Gomega) *rayv1alpha1.RayCluster {

test/support/ray_cluster_client.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package support
1919
import (
2020
"bytes"
2121
"encoding/json"
22-
"io/ioutil"
22+
"io"
2323
"net/http"
2424
"net/url"
2525
)
@@ -72,7 +72,7 @@ func (client *rayClusterClient) CreateJob(job *RayJobSetup) (response *RayJobRes
7272
return
7373
}
7474

75-
respData, err := ioutil.ReadAll(resp.Body)
75+
respData, err := io.ReadAll(resp.Body)
7676
if err != nil {
7777
return
7878
}
@@ -88,7 +88,7 @@ func (client *rayClusterClient) GetJobDetails(jobID string) (response *RayJobDet
8888
return
8989
}
9090

91-
respData, err := ioutil.ReadAll(resp.Body)
91+
respData, err := io.ReadAll(resp.Body)
9292
if err != nil {
9393
return
9494
}
@@ -104,7 +104,7 @@ func (client *rayClusterClient) GetJobLogs(jobID string) (logs string, err error
104104
return
105105
}
106106

107-
respData, err := ioutil.ReadAll(resp.Body)
107+
respData, err := io.ReadAll(resp.Body)
108108
if err != nil {
109109
return
110110
}

test/support/route.go

+5
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ func Route(t Test, namespace, name string) func(g gomega.Gomega) *routev1.Route
3131
return route
3232
}
3333
}
34+
35+
func GetRoute(t Test, namespace, name string) *routev1.Route {
36+
t.T().Helper()
37+
return Route(t, namespace, name)(t)
38+
}

0 commit comments

Comments
 (0)