Skip to content

Commit 7aa79e8

Browse files
Bobbins228openshift-merge-bot[bot]
authored andcommittedOct 9, 2024
refactor: codeflare sdk unit tests
sort unit tests into individual files and made minor enhancements
1 parent bff13a5 commit 7aa79e8

28 files changed

+3455
-3573
lines changed
 

‎pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ markers = [
5757
"nvidia_gpu"
5858
]
5959
addopts = "--timeout=900"
60+
testpaths = ["src/codeflare_sdk"]
61+
collect_ignore = ["src/codeflare_sdk/common/utils/unit_test_support.py"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Copyright 2024 IBM, Red Hat
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from codeflare_sdk.common.kubernetes_cluster import (
16+
Authentication,
17+
KubeConfigFileAuthentication,
18+
TokenAuthentication,
19+
config_check,
20+
)
21+
from kubernetes import client, config
22+
import os
23+
from pathlib import Path
24+
import pytest
25+
26+
parent = Path(__file__).resolve().parents[4] # project directory
27+
28+
29+
def test_token_auth_creation():
30+
token_auth = TokenAuthentication(token="token", server="server")
31+
assert token_auth.token == "token"
32+
assert token_auth.server == "server"
33+
assert token_auth.skip_tls == False
34+
assert token_auth.ca_cert_path == None
35+
36+
token_auth = TokenAuthentication(token="token", server="server", skip_tls=True)
37+
assert token_auth.token == "token"
38+
assert token_auth.server == "server"
39+
assert token_auth.skip_tls == True
40+
assert token_auth.ca_cert_path == None
41+
42+
os.environ["CF_SDK_CA_CERT_PATH"] = "/etc/pki/tls/custom-certs/ca-bundle.crt"
43+
token_auth = TokenAuthentication(token="token", server="server", skip_tls=False)
44+
assert token_auth.token == "token"
45+
assert token_auth.server == "server"
46+
assert token_auth.skip_tls == False
47+
assert token_auth.ca_cert_path == "/etc/pki/tls/custom-certs/ca-bundle.crt"
48+
os.environ.pop("CF_SDK_CA_CERT_PATH")
49+
50+
token_auth = TokenAuthentication(
51+
token="token",
52+
server="server",
53+
skip_tls=False,
54+
ca_cert_path=f"{parent}/tests/auth-test.crt",
55+
)
56+
assert token_auth.token == "token"
57+
assert token_auth.server == "server"
58+
assert token_auth.skip_tls == False
59+
assert token_auth.ca_cert_path == f"{parent}/tests/auth-test.crt"
60+
61+
62+
def test_token_auth_login_logout(mocker):
63+
mocker.patch.object(client, "ApiClient")
64+
65+
token_auth = TokenAuthentication(
66+
token="testtoken", server="testserver:6443", skip_tls=False, ca_cert_path=None
67+
)
68+
assert token_auth.login() == ("Logged into testserver:6443")
69+
assert token_auth.logout() == ("Successfully logged out of testserver:6443")
70+
71+
72+
def test_token_auth_login_tls(mocker):
73+
mocker.patch.object(client, "ApiClient")
74+
75+
token_auth = TokenAuthentication(
76+
token="testtoken", server="testserver:6443", skip_tls=True, ca_cert_path=None
77+
)
78+
assert token_auth.login() == ("Logged into testserver:6443")
79+
token_auth = TokenAuthentication(
80+
token="testtoken", server="testserver:6443", skip_tls=False, ca_cert_path=None
81+
)
82+
assert token_auth.login() == ("Logged into testserver:6443")
83+
token_auth = TokenAuthentication(
84+
token="testtoken",
85+
server="testserver:6443",
86+
skip_tls=False,
87+
ca_cert_path=f"{parent}/tests/auth-test.crt",
88+
)
89+
assert token_auth.login() == ("Logged into testserver:6443")
90+
91+
os.environ["CF_SDK_CA_CERT_PATH"] = f"{parent}/tests/auth-test.crt"
92+
token_auth = TokenAuthentication(
93+
token="testtoken",
94+
server="testserver:6443",
95+
skip_tls=False,
96+
)
97+
assert token_auth.login() == ("Logged into testserver:6443")
98+
99+
100+
def test_config_check_no_config_file(mocker):
101+
mocker.patch("os.path.expanduser", return_value="/mock/home/directory")
102+
mocker.patch("os.path.isfile", return_value=False)
103+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.config_path", None)
104+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.api_client", None)
105+
106+
with pytest.raises(PermissionError):
107+
config_check()
108+
109+
110+
def test_config_check_with_incluster_config(mocker):
111+
mocker.patch("os.path.expanduser", return_value="/mock/home/directory")
112+
mocker.patch("os.path.isfile", return_value=False)
113+
mocker.patch.dict(os.environ, {"KUBERNETES_PORT": "number"})
114+
mocker.patch("kubernetes.config.load_incluster_config", side_effect=None)
115+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.config_path", None)
116+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.api_client", None)
117+
118+
result = config_check()
119+
assert result == None
120+
121+
122+
def test_config_check_with_existing_config_file(mocker):
123+
mocker.patch("os.path.expanduser", return_value="/mock/home/directory")
124+
mocker.patch("os.path.isfile", return_value=True)
125+
mocker.patch("kubernetes.config.load_kube_config", side_effect=None)
126+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.config_path", None)
127+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.api_client", None)
128+
129+
result = config_check()
130+
assert result == None
131+
132+
133+
def test_config_check_with_config_path_and_no_api_client(mocker):
134+
mocker.patch(
135+
"codeflare_sdk.common.kubernetes_cluster.auth.config_path", "/mock/config/path"
136+
)
137+
mocker.patch("codeflare_sdk.common.kubernetes_cluster.auth.api_client", None)
138+
result = config_check()
139+
assert result == "/mock/config/path"
140+
141+
142+
def test_load_kube_config(mocker):
143+
mocker.patch.object(config, "load_kube_config")
144+
kube_config_auth = KubeConfigFileAuthentication(
145+
kube_config_path="/path/to/your/config"
146+
)
147+
response = kube_config_auth.load_kube_config()
148+
149+
assert (
150+
response
151+
== "Loaded user config file at path %s" % kube_config_auth.kube_config_path
152+
)
153+
154+
kube_config_auth = KubeConfigFileAuthentication(kube_config_path=None)
155+
response = kube_config_auth.load_kube_config()
156+
assert response == "Please specify a config file path"
157+
158+
159+
def test_auth_coverage():
160+
abstract = Authentication()
161+
abstract.login()
162+
abstract.logout()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Copyright 2024 IBM, Red Hat
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from ..utils.unit_test_support import get_local_queue, createClusterConfig
15+
from unittest.mock import patch
16+
from codeflare_sdk.ray.cluster.cluster import Cluster, ClusterConfiguration
17+
import yaml
18+
import os
19+
import filecmp
20+
from pathlib import Path
21+
22+
parent = Path(__file__).resolve().parents[4] # project directory
23+
aw_dir = os.path.expanduser("~/.codeflare/resources/")
24+
25+
26+
def test_none_local_queue(mocker):
27+
mocker.patch("kubernetes.client.CustomObjectsApi.list_namespaced_custom_object")
28+
config = ClusterConfiguration(name="unit-test-aw-kueue", namespace="ns")
29+
config.name = "unit-test-aw-kueue"
30+
config.local_queue = None
31+
32+
cluster = Cluster(config)
33+
assert cluster.config.local_queue == None
34+
35+
36+
def test_cluster_creation_no_aw_local_queue(mocker):
37+
# With written resources
38+
# Create Ray Cluster with local queue specified
39+
mocker.patch("kubernetes.client.ApisApi.get_api_versions")
40+
mocker.patch(
41+
"kubernetes.client.CustomObjectsApi.get_cluster_custom_object",
42+
return_value={"spec": {"domain": "apps.cluster.awsroute.org"}},
43+
)
44+
mocker.patch(
45+
"kubernetes.client.CustomObjectsApi.list_namespaced_custom_object",
46+
return_value=get_local_queue("kueue.x-k8s.io", "v1beta1", "ns", "localqueues"),
47+
)
48+
config = createClusterConfig()
49+
config.name = "unit-test-cluster-kueue"
50+
config.write_to_file = True
51+
config.local_queue = "local-queue-default"
52+
cluster = Cluster(config)
53+
assert cluster.app_wrapper_yaml == f"{aw_dir}unit-test-cluster-kueue.yaml"
54+
assert cluster.app_wrapper_name == "unit-test-cluster-kueue"
55+
assert filecmp.cmp(
56+
f"{aw_dir}unit-test-cluster-kueue.yaml",
57+
f"{parent}/tests/test_cluster_yamls/kueue/ray_cluster_kueue.yaml",
58+
shallow=True,
59+
)
60+
61+
# With resources loaded in memory, no Local Queue specified.
62+
config = createClusterConfig()
63+
config.name = "unit-test-cluster-kueue"
64+
config.write_to_file = False
65+
cluster = Cluster(config)
66+
67+
test_rc = yaml.load(cluster.app_wrapper_yaml, Loader=yaml.FullLoader)
68+
with open(f"{parent}/tests/test_cluster_yamls/kueue/ray_cluster_kueue.yaml") as f:
69+
expected_rc = yaml.load(f, Loader=yaml.FullLoader)
70+
assert test_rc == expected_rc
71+
72+
73+
def test_aw_creation_local_queue(mocker):
74+
mocker.patch("kubernetes.client.ApisApi.get_api_versions")
75+
mocker.patch(
76+
"kubernetes.client.CustomObjectsApi.get_cluster_custom_object",
77+
return_value={"spec": {"domain": "apps.cluster.awsroute.org"}},
78+
)
79+
mocker.patch(
80+
"kubernetes.client.CustomObjectsApi.list_namespaced_custom_object",
81+
return_value=get_local_queue("kueue.x-k8s.io", "v1beta1", "ns", "localqueues"),
82+
)
83+
config = createClusterConfig()
84+
config.name = "unit-test-aw-kueue"
85+
config.appwrapper = True
86+
config.write_to_file = True
87+
config.local_queue = "local-queue-default"
88+
cluster = Cluster(config)
89+
assert cluster.app_wrapper_yaml == f"{aw_dir}unit-test-aw-kueue.yaml"
90+
assert cluster.app_wrapper_name == "unit-test-aw-kueue"
91+
assert filecmp.cmp(
92+
f"{aw_dir}unit-test-aw-kueue.yaml",
93+
f"{parent}/tests/test_cluster_yamls/kueue/aw_kueue.yaml",
94+
shallow=True,
95+
)
96+
97+
# With resources loaded in memory, no Local Queue specified.
98+
config = createClusterConfig()
99+
config.name = "unit-test-aw-kueue"
100+
config.appwrapper = True
101+
config.write_to_file = False
102+
cluster = Cluster(config)
103+
104+
test_rc = yaml.load(cluster.app_wrapper_yaml, Loader=yaml.FullLoader)
105+
with open(f"{parent}/tests/test_cluster_yamls/kueue/aw_kueue.yaml") as f:
106+
expected_rc = yaml.load(f, Loader=yaml.FullLoader)
107+
assert test_rc == expected_rc
108+
109+
110+
def test_get_local_queue_exists_fail(mocker):
111+
mocker.patch("kubernetes.client.ApisApi.get_api_versions")
112+
mocker.patch(
113+
"kubernetes.client.CustomObjectsApi.get_cluster_custom_object",
114+
return_value={"spec": {"domain": "apps.cluster.awsroute.org"}},
115+
)
116+
mocker.patch(
117+
"kubernetes.client.CustomObjectsApi.list_namespaced_custom_object",
118+
return_value=get_local_queue("kueue.x-k8s.io", "v1beta1", "ns", "localqueues"),
119+
)
120+
config = createClusterConfig()
121+
config.name = "unit-test-aw-kueue"
122+
config.appwrapper = True
123+
config.write_to_file = True
124+
config.local_queue = "local_queue_doesn't_exist"
125+
try:
126+
Cluster(config)
127+
except ValueError as e:
128+
assert (
129+
str(e)
130+
== "local_queue provided does not exist or is not in this namespace. Please provide the correct local_queue name in Cluster Configuration"
131+
)
132+
133+
134+
# Make sure to always keep this function last
135+
def test_cleanup():
136+
os.remove(f"{aw_dir}unit-test-cluster-kueue.yaml")
137+
os.remove(f"{aw_dir}unit-test-aw-kueue.yaml")

0 commit comments

Comments
 (0)