1
+ import copy
1
2
import os
2
3
import sys
3
4
import pytest
4
5
import subprocess
6
+ import json
5
7
6
8
import ray
7
9
from ray .cluster_utils import AutoscalingCluster
8
10
from ray ._private .test_utils import wait_for_condition
9
11
12
+ LONG_STR_VALID = "" .join ("a" for _ in range (ray ._raylet .RAY_LABEL_MAX_LENGTH - 1 ))
13
+ LONG_STR_EXCEEDED = "" .join ("a" for _ in range (ray ._raylet .RAY_LABEL_MAX_LENGTH ))
14
+ ALL_LETTERS_VALID_STR = "abc.com/1234567890-_.abcdefghijklmnopqrstuvwxyzABC"
15
+ VALID_LABELS = {
16
+ "gpu_type" :"A100" ,
17
+ "region" :"us" ,
18
+ ALL_LETTERS_VALID_STR :ALL_LETTERS_VALID_STR ,
19
+ LONG_STR_VALID :LONG_STR_VALID ,
20
+ "empty_value" :"" ,
21
+ }
22
+ VALID_LABELS_JSON = json .dumps (VALID_LABELS ,separators = ("," ,":" ))
23
+
24
+ ILLEGA_LENGTH_PROMPT = (
25
+ f"cannot be empty or exceed{ ray ._raylet .RAY_LABEL_MAX_LENGTH - 1 } characters"
26
+ )
27
+ ILLEGA_LETTERS_PROMPT = (
28
+ "consist of letters from `a-z0-9A-Z` or `-` or `_` or `.` or `/`"
29
+ )
30
+
10
31
11
32
def check_cmd_stderr (cmd ):
12
33
return subprocess .run (cmd ,stderr = subprocess .PIPE ).stderr .decode ("utf-8" )
13
34
14
35
15
36
def add_default_labels (node_info ,labels ):
16
- labels ["ray.io/node_id" ]= node_info ["NodeID" ]
17
- return labels
37
+ final_labels = copy .deepcopy (labels )
38
+ final_labels ["ray.io/node_id" ]= node_info ["NodeID" ]
39
+ return final_labels
18
40
19
41
20
42
@pytest .mark .parametrize (
21
43
"call_ray_start" ,
22
- [' ray start --head --labels={"gpu_type":"A100","region":"us"}' ],
44
+ [f" ray start --head --labels={ VALID_LABELS_JSON } " ],
23
45
indirect = True ,
24
46
)
25
47
def test_ray_start_set_node_labels (call_ray_start ):
26
48
ray .init (address = call_ray_start )
27
49
node_info = ray .nodes ()[0 ]
28
- assert node_info ["Labels" ]== add_default_labels (
29
- node_info , {"gpu_type" :"A100" ,"region" :"us" }
30
- )
50
+ assert node_info ["Labels" ]== add_default_labels (node_info ,VALID_LABELS )
31
51
32
52
33
53
@pytest .mark .parametrize (
@@ -44,10 +64,9 @@ def test_ray_start_set_empty_node_labels(call_ray_start):
44
64
45
65
46
66
def test_ray_init_set_node_labels (shutdown_only ):
47
- labels = {"gpu_type" :"A100" ,"region" :"us" }
48
- ray .init (labels = labels )
67
+ ray .init (labels = VALID_LABELS )
49
68
node_info = ray .nodes ()[0 ]
50
- assert node_info ["Labels" ]== add_default_labels (node_info ,labels )
69
+ assert node_info ["Labels" ]== add_default_labels (node_info ,VALID_LABELS )
51
70
ray .shutdown ()
52
71
ray .init (labels = {})
53
72
node_info = ray .nodes ()[0 ]
@@ -57,20 +76,55 @@ def test_ray_init_set_node_labels(shutdown_only):
57
76
def test_ray_init_set_node_labels_value_error (ray_start_cluster ):
58
77
cluster = ray_start_cluster
59
78
79
+ # cluster.add_node api of node labels.
60
80
key = "ray.io/node_id"
61
81
with pytest .raises (
62
82
ValueError ,
63
83
match = f"Custom label keys `{ key } ` cannot start with the prefix `ray.io/`" ,
64
84
):
65
85
cluster .add_node (num_cpus = 1 ,labels = {key :"111111" })
66
86
87
+ with pytest .raises (
88
+ ValueError ,
89
+ match = ILLEGA_LENGTH_PROMPT ,
90
+ ):
91
+ cluster .add_node (num_cpus = 1 ,labels = {LONG_STR_EXCEEDED :"111111" })
92
+
93
+ with pytest .raises (
94
+ ValueError ,
95
+ match = ILLEGA_LETTERS_PROMPT ,
96
+ ):
97
+ cluster .add_node (num_cpus = 1 ,labels = {"key" :"aa%$22" })
98
+
99
+ # ray.init api of node labels.
67
100
key = "ray.io/other_key"
68
101
with pytest .raises (
69
102
ValueError ,
70
103
match = f"Custom label keys `{ key } ` cannot start with the prefix `ray.io/`" ,
71
104
):
72
105
ray .init (labels = {key :"value" })
73
106
107
+ with pytest .raises (ValueError ,match = ILLEGA_LENGTH_PROMPT ):
108
+ ray .init (labels = {LONG_STR_EXCEEDED :"123" })
109
+
110
+ with pytest .raises (ValueError ,match = ILLEGA_LENGTH_PROMPT ):
111
+ ray .init (labels = {"" :"123" })
112
+
113
+ with pytest .raises (ValueError ,match = ILLEGA_LENGTH_PROMPT ):
114
+ ray .init (labels = {"key" :LONG_STR_EXCEEDED })
115
+
116
+ with pytest .raises (
117
+ ValueError ,
118
+ match = ILLEGA_LETTERS_PROMPT ,
119
+ ):
120
+ ray .init (labels = {"key" :"abc@ss" })
121
+
122
+ with pytest .raises (
123
+ ValueError ,
124
+ match = ILLEGA_LETTERS_PROMPT ,
125
+ ):
126
+ ray .init (labels = {"k y" :"123" })
127
+
74
128
cluster .add_node (num_cpus = 1 )
75
129
with pytest .raises (ValueError ,match = "labels must not be provided" ):
76
130
ray .init (address = cluster .address ,labels = {"gpu_type" :"A100" })
@@ -96,15 +150,35 @@ def test_ray_start_set_node_labels_value_error():
96
150
)
97
151
assert "cannot start with the prefix `ray.io/`" in out
98
152
153
+ # Validate illegal length of keys and values.
154
+ out = check_cmd_stderr (
155
+ ["ray" ,"start" ,"--head" ,f'--labels={{"{ LONG_STR_EXCEEDED } ":"111"}}' ]
156
+ )
157
+ assert ILLEGA_LENGTH_PROMPT in out
158
+
159
+ out = check_cmd_stderr (
160
+ ["ray" ,"start" ,"--head" ,f'--labels={{"key":"{ LONG_STR_EXCEEDED } "}}' ]
161
+ )
162
+ assert ILLEGA_LENGTH_PROMPT in out
163
+
164
+ out = check_cmd_stderr (["ray" ,"start" ,"--head" ,'--labels={"":"111"}' ])
165
+ assert ILLEGA_LENGTH_PROMPT in out
166
+
167
+ # Validate illegal letters of keys and values.
168
+ out = check_cmd_stderr (["ray" ,"start" ,"--head" ,'--labels={"?$#s":"123"}' ])
169
+ assert ILLEGA_LETTERS_PROMPT in out
170
+
171
+ out = check_cmd_stderr (["ray" ,"start" ,"--head" ,'--labels={"key":"`1~~"}' ])
172
+ assert ILLEGA_LETTERS_PROMPT in out
173
+
99
174
100
175
def test_cluster_add_node_with_labels (ray_start_cluster ):
101
- labels = {"gpu_type" :"A100" ,"region" :"us" }
102
176
cluster = ray_start_cluster
103
- cluster .add_node (num_cpus = 1 ,labels = labels )
177
+ cluster .add_node (num_cpus = 1 ,labels = VALID_LABELS )
104
178
cluster .wait_for_nodes ()
105
179
ray .init (address = cluster .address )
106
180
node_info = ray .nodes ()[0 ]
107
- assert node_info ["Labels" ]== add_default_labels (node_info ,labels )
181
+ assert node_info ["Labels" ]== add_default_labels (node_info ,VALID_LABELS )
108
182
head_node_id = ray .nodes ()[0 ]["NodeID" ]
109
183
110
184
cluster .add_node (num_cpus = 1 ,labels = {})