Skip to content

Commit 4366322

Browse files
sutaakaropenshift-merge-bot[bot]
authored andcommitted
Setup existing DW tests to run in CFO
1 parent 72cace9 commit 4366322

10 files changed

+924
-0
lines changed

test/odh/environment.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 odh
18+
19+
import (
20+
"os"
21+
22+
. "github.com/project-codeflare/codeflare-common/support"
23+
)
24+
25+
const (
26+
// The environment variable for namespace where ODH is installed to.
27+
odhNamespaceEnvVar = "ODH_NAMESPACE"
28+
// The environment variable for ODH Notebook ImageStream name
29+
notebookImageStreamName = "NOTEBOOK_IMAGE_STREAM_NAME"
30+
)
31+
32+
func GetOpenDataHubNamespace() string {
33+
return lookupEnvOrDefault(odhNamespaceEnvVar, "opendatahub")
34+
}
35+
36+
func GetNotebookImageStreamName(t Test) string {
37+
isName, ok := os.LookupEnv(notebookImageStreamName)
38+
if !ok {
39+
t.T().Fatalf("Expected environment variable %s not found, please use this environment variable to specify what ImageStream to use for Notebook.", notebookImageStreamName)
40+
}
41+
return isName
42+
}
43+
44+
func lookupEnvOrDefault(key, value string) string {
45+
if v, ok := os.LookupEnv(key); ok {
46+
return v
47+
}
48+
return value
49+
}

test/odh/mcad_ray_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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 odh
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
. "github.com/project-codeflare/codeflare-common/support"
24+
mcadv1beta1 "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/apis/controller/v1beta1"
25+
rayv1alpha1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1alpha1"
26+
27+
rbacv1 "k8s.io/api/rbac/v1"
28+
)
29+
30+
func TestMCADRay(t *testing.T) {
31+
test := With(t)
32+
33+
// Create a namespace
34+
namespace := test.NewTestNamespace()
35+
36+
// Test configuration
37+
jupyterNotebookConfigMapFileName := "mnist_ray_mini.ipynb"
38+
config := CreateConfigMap(test, namespace.Name, map[string][]byte{
39+
// MNIST Ray Notebook
40+
jupyterNotebookConfigMapFileName: ReadFile(test, "resources/mnist_ray_mini.ipynb"),
41+
"mnist.py": ReadFile(test, "resources/mnist.py"),
42+
"requirements.txt": ReadFile(test, "resources/requirements.txt"),
43+
})
44+
45+
// Create RBAC, retrieve token for user with limited rights
46+
policyRules := []rbacv1.PolicyRule{
47+
{
48+
Verbs: []string{"get", "create", "delete", "list", "patch", "update"},
49+
APIGroups: []string{mcadv1beta1.GroupName},
50+
Resources: []string{"appwrappers"},
51+
},
52+
{
53+
Verbs: []string{"get", "list"},
54+
APIGroups: []string{rayv1alpha1.GroupVersion.Group},
55+
Resources: []string{"rayclusters", "rayclusters/status"},
56+
},
57+
{
58+
Verbs: []string{"get", "list"},
59+
APIGroups: []string{"route.openshift.io"},
60+
Resources: []string{"routes"},
61+
},
62+
}
63+
64+
// Create cluster wide RBAC, required for SDK OpenShift check
65+
// TODO reevaluate once SDK change OpenShift detection logic
66+
clusterPolicyRules := []rbacv1.PolicyRule{
67+
{
68+
Verbs: []string{"get", "list"},
69+
APIGroups: []string{"config.openshift.io"},
70+
Resources: []string{"ingresses"},
71+
ResourceNames: []string{"cluster"},
72+
},
73+
}
74+
75+
sa := CreateServiceAccount(test, namespace.Name)
76+
role := CreateRole(test, namespace.Name, policyRules)
77+
CreateRoleBinding(test, namespace.Name, sa, role)
78+
clusterRole := CreateClusterRole(test, clusterPolicyRules)
79+
CreateClusterRoleBinding(test, sa, clusterRole)
80+
token := CreateToken(test, namespace.Name, sa)
81+
82+
// Create Notebook CR
83+
createNotebook(test, namespace, token, config.Name, jupyterNotebookConfigMapFileName)
84+
85+
// Make sure the AppWrapper is created and running
86+
test.Eventually(AppWrappers(test, namespace), TestTimeoutLong).
87+
Should(
88+
And(
89+
HaveLen(1),
90+
ContainElement(WithTransform(AppWrapperName, HavePrefix("mnisttest"))),
91+
ContainElement(WithTransform(AppWrapperState, Equal(mcadv1beta1.AppWrapperStateActive))),
92+
),
93+
)
94+
95+
// Make sure the AppWrapper finishes and is deleted
96+
test.Eventually(AppWrappers(test, namespace), TestTimeoutLong).
97+
Should(HaveLen(0))
98+
}

test/odh/notebook.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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 odh
18+
19+
import (
20+
"bytes"
21+
"html/template"
22+
23+
gomega "github.com/onsi/gomega"
24+
. "github.com/project-codeflare/codeflare-common/support"
25+
26+
corev1 "k8s.io/api/core/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29+
"k8s.io/apimachinery/pkg/runtime/schema"
30+
"k8s.io/apimachinery/pkg/util/yaml"
31+
32+
imagev1 "github.com/openshift/api/image/v1"
33+
)
34+
35+
const recommendedTagAnnotation = "opendatahub.io/workbench-image-recommended"
36+
37+
var notebookResource = schema.GroupVersionResource{Group: "kubeflow.org", Version: "v1", Resource: "notebooks"}
38+
39+
type NotebookProps struct {
40+
IngressDomain string
41+
OpenShiftApiUrl string
42+
KubernetesBearerToken string
43+
Namespace string
44+
OpenDataHubNamespace string
45+
ImageStreamName string
46+
ImageStreamTag string
47+
NotebookConfigMapName string
48+
NotebookConfigMapFileName string
49+
NotebookPVC string
50+
}
51+
52+
func createNotebook(test Test, namespace *corev1.Namespace, notebookToken, jupyterNotebookConfigMapName, jupyterNotebookConfigMapFileName string) {
53+
// Create PVC for Notebook
54+
notebookPVC := CreatePersistentVolumeClaim(test, namespace.Name, "10Gi", corev1.ReadWriteOnce)
55+
56+
// Retrieve ImageStream tag for
57+
is := GetImageStream(test, GetOpenDataHubNamespace(), GetNotebookImageStreamName(test))
58+
recommendedTagName := getRecommendedImageStreamTag(test, is)
59+
60+
// Read the Notebook CR from resources and perform replacements for custom values using go template
61+
notebookProps := NotebookProps{
62+
IngressDomain: GetOpenShiftIngressDomain(test),
63+
OpenShiftApiUrl: GetOpenShiftApiUrl(test),
64+
KubernetesBearerToken: notebookToken,
65+
Namespace: namespace.Name,
66+
OpenDataHubNamespace: GetOpenDataHubNamespace(),
67+
ImageStreamName: GetNotebookImageStreamName(test),
68+
ImageStreamTag: recommendedTagName,
69+
NotebookConfigMapName: jupyterNotebookConfigMapName,
70+
NotebookConfigMapFileName: jupyterNotebookConfigMapFileName,
71+
NotebookPVC: notebookPVC.Name,
72+
}
73+
notebookTemplate, err := files.ReadFile("resources/custom-nb-small.yaml")
74+
test.Expect(err).NotTo(gomega.HaveOccurred())
75+
parsedNotebookTemplate, err := template.New("notebook").Parse(string(notebookTemplate))
76+
test.Expect(err).NotTo(gomega.HaveOccurred())
77+
78+
// Filter template and store results to the buffer
79+
notebookBuffer := new(bytes.Buffer)
80+
err = parsedNotebookTemplate.Execute(notebookBuffer, notebookProps)
81+
test.Expect(err).NotTo(gomega.HaveOccurred())
82+
83+
// Create Notebook CR
84+
notebookCR := &unstructured.Unstructured{}
85+
err = yaml.NewYAMLOrJSONDecoder(notebookBuffer, 8192).Decode(notebookCR)
86+
test.Expect(err).NotTo(gomega.HaveOccurred())
87+
_, err = test.Client().Dynamic().Resource(notebookResource).Namespace(namespace.Name).Create(test.Ctx(), notebookCR, metav1.CreateOptions{})
88+
test.Expect(err).NotTo(gomega.HaveOccurred())
89+
}
90+
91+
func getRecommendedImageStreamTag(test Test, is *imagev1.ImageStream) (tagName string) {
92+
for _, tag := range is.Spec.Tags {
93+
if tag.Annotations[recommendedTagAnnotation] == "true" {
94+
return tag.Name
95+
}
96+
}
97+
test.T().Fatalf("tag with annotation '%s' not found in ImageStream %s", recommendedTagAnnotation, is.Name)
98+
return
99+
}

test/odh/pytorch_mcad_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 odh
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
. "github.com/project-codeflare/codeflare-common/support"
24+
mcadv1beta1 "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/apis/controller/v1beta1"
25+
26+
rbacv1 "k8s.io/api/rbac/v1"
27+
)
28+
29+
func TestMnistPyTorchMCAD(t *testing.T) {
30+
test := With(t)
31+
32+
// Create a namespace
33+
namespace := test.NewTestNamespace()
34+
35+
// Test configuration
36+
jupyterNotebookConfigMapFileName := "mnist_mcad_mini.ipynb"
37+
config := CreateConfigMap(test, namespace.Name, map[string][]byte{
38+
// MNIST MCAD Notebook
39+
jupyterNotebookConfigMapFileName: ReadFile(test, "resources/mnist_mcad_mini.ipynb"),
40+
})
41+
42+
// Create RBAC, retrieve token for user with limited rights
43+
policyRules := []rbacv1.PolicyRule{
44+
{
45+
Verbs: []string{"get", "create", "delete", "list", "patch", "update"},
46+
APIGroups: []string{mcadv1beta1.GroupName},
47+
Resources: []string{"appwrappers"},
48+
},
49+
// Needed for job.logs()
50+
{
51+
Verbs: []string{"get"},
52+
APIGroups: []string{""},
53+
Resources: []string{"pods/log"},
54+
},
55+
}
56+
sa := CreateServiceAccount(test, namespace.Name)
57+
role := CreateRole(test, namespace.Name, policyRules)
58+
CreateRoleBinding(test, namespace.Name, sa, role)
59+
token := CreateToken(test, namespace.Name, sa)
60+
61+
// Create Notebook CR
62+
createNotebook(test, namespace, token, config.Name, jupyterNotebookConfigMapFileName)
63+
64+
// Make sure the AppWrapper is created and running
65+
test.Eventually(AppWrappers(test, namespace), TestTimeoutLong).
66+
Should(
67+
And(
68+
HaveLen(1),
69+
ContainElement(WithTransform(AppWrapperName, HavePrefix("mnistjob"))),
70+
ContainElement(WithTransform(AppWrapperState, Equal(mcadv1beta1.AppWrapperStateActive))),
71+
),
72+
)
73+
74+
// Make sure the AppWrapper finishes and is deleted
75+
test.Eventually(AppWrappers(test, namespace), TestTimeoutLong).
76+
Should(HaveLen(0))
77+
}

0 commit comments

Comments
 (0)