mirror of
https://github.com/wassname/ray.git
synced 2026-07-08 21:03:37 +08:00
206 lines
7.1 KiB
Python
206 lines
7.1 KiB
Python
import jsonschema
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import urllib
|
|
import yaml
|
|
import copy
|
|
from unittest.mock import MagicMock, Mock, patch
|
|
import pytest
|
|
|
|
from ray.autoscaler._private.util import prepare_config, validate_config
|
|
from ray.autoscaler._private.providers import _NODE_PROVIDERS
|
|
|
|
from ray.test_utils import recursive_fnmatch
|
|
|
|
RAY_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
|
CONFIG_PATHS = recursive_fnmatch(
|
|
os.path.join(RAY_PATH, "autoscaler"), "*.yaml")
|
|
|
|
CONFIG_PATHS += recursive_fnmatch(
|
|
os.path.join(RAY_PATH, "tune", "examples"), "*.yaml")
|
|
|
|
|
|
def ignore_k8s_operator_configs(paths):
|
|
return [
|
|
path for path in paths if "kubernetes/operator_configs" not in path
|
|
]
|
|
|
|
|
|
CONFIG_PATHS = ignore_k8s_operator_configs(CONFIG_PATHS)
|
|
|
|
|
|
class AutoscalingConfigTest(unittest.TestCase):
|
|
def testValidateDefaultConfig(self):
|
|
for config_path in CONFIG_PATHS:
|
|
if "aws/example-multi-node-type.yaml" in config_path:
|
|
# aws is tested in testValidateDefaultConfigAWSMultiNodeTypes.
|
|
continue
|
|
with open(config_path) as f:
|
|
config = yaml.safe_load(f)
|
|
config = prepare_config(config)
|
|
try:
|
|
validate_config(config)
|
|
except Exception:
|
|
self.fail("Config did not pass validation test!")
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform.startswith("win"),
|
|
reason="TODO(ameer): fails on Windows.")
|
|
def testValidateDefaultConfigAWSMultiNodeTypes(self):
|
|
aws_config_path = os.path.join(
|
|
RAY_PATH, "autoscaler/aws/example-multi-node-type.yaml")
|
|
with open(aws_config_path) as f:
|
|
config = yaml.safe_load(f)
|
|
new_config = copy.deepcopy(config)
|
|
# modify it here
|
|
new_config["available_node_types"] = {
|
|
"cpu_4_ondemand": new_config["available_node_types"][
|
|
"cpu_4_ondemand"],
|
|
"cpu_16_spot": new_config["available_node_types"]["cpu_16_spot"],
|
|
"gpu_8_ondemand": new_config["available_node_types"][
|
|
"gpu_8_ondemand"]
|
|
}
|
|
orig_new_config = copy.deepcopy(new_config)
|
|
expected_available_node_types = orig_new_config["available_node_types"]
|
|
expected_available_node_types["cpu_4_ondemand"]["resources"] = {
|
|
"CPU": 4
|
|
}
|
|
expected_available_node_types["cpu_16_spot"]["resources"] = {
|
|
"CPU": 16,
|
|
"Custom1": 1,
|
|
"is_spot": 1
|
|
}
|
|
expected_available_node_types["gpu_8_ondemand"]["resources"] = {
|
|
"CPU": 32,
|
|
"GPU": 4,
|
|
"accelerator_type:V100": 1
|
|
}
|
|
|
|
boto3_dict = {
|
|
"InstanceTypes": [{
|
|
"InstanceType": "m4.xlarge",
|
|
"VCpuInfo": {
|
|
"DefaultVCpus": 4
|
|
}
|
|
}, {
|
|
"InstanceType": "m4.4xlarge",
|
|
"VCpuInfo": {
|
|
"DefaultVCpus": 16
|
|
}
|
|
}, {
|
|
"InstanceType": "p3.8xlarge",
|
|
"VCpuInfo": {
|
|
"DefaultVCpus": 32
|
|
},
|
|
"GpuInfo": {
|
|
"Gpus": [{
|
|
"Name": "V100",
|
|
"Count": 4
|
|
}]
|
|
}
|
|
}]
|
|
}
|
|
boto3_mock = Mock()
|
|
describe_instance_types_mock = Mock()
|
|
describe_instance_types_mock.describe_instance_types = MagicMock(
|
|
return_value=boto3_dict)
|
|
boto3_mock.client = MagicMock(
|
|
return_value=describe_instance_types_mock)
|
|
with patch.multiple(
|
|
"ray.autoscaler._private.aws.node_provider",
|
|
boto3=boto3_mock,
|
|
):
|
|
new_config = prepare_config(new_config)
|
|
importer = _NODE_PROVIDERS.get(new_config["provider"]["type"])
|
|
provider_cls = importer(new_config["provider"])
|
|
|
|
try:
|
|
new_config = \
|
|
provider_cls.fillout_available_node_types_resources(
|
|
new_config)
|
|
validate_config(new_config)
|
|
expected_available_node_types == new_config[
|
|
"available_node_types"]
|
|
except Exception:
|
|
self.fail(
|
|
"Config did not pass multi node types auto fill test!")
|
|
|
|
def testValidateNetworkConfig(self):
|
|
web_yaml = "https://raw.githubusercontent.com/ray-project/ray/" \
|
|
"master/python/ray/autoscaler/aws/example-full.yaml"
|
|
response = urllib.request.urlopen(web_yaml, timeout=5)
|
|
content = response.read()
|
|
with tempfile.TemporaryFile() as f:
|
|
f.write(content)
|
|
f.seek(0)
|
|
config = yaml.safe_load(f)
|
|
config = prepare_config(config)
|
|
try:
|
|
validate_config(config)
|
|
except Exception:
|
|
self.fail("Config did not pass validation test!")
|
|
|
|
def _test_invalid_config(self, config_path):
|
|
with open(os.path.join(RAY_PATH, config_path)) as f:
|
|
config = yaml.safe_load(f)
|
|
try:
|
|
validate_config(config)
|
|
self.fail("Expected validation to fail for {}".format(config_path))
|
|
except jsonschema.ValidationError:
|
|
pass
|
|
|
|
@unittest.skipIf(sys.platform == "win32", "Failing on Windows.")
|
|
def testInvalidConfig(self):
|
|
self._test_invalid_config(
|
|
os.path.join("tests", "additional_property.yaml"))
|
|
|
|
@unittest.skipIf(sys.platform == "win32", "Failing on Windows.")
|
|
def testValidateCustomSecurityGroupConfig(self):
|
|
aws_config_path = os.path.join(RAY_PATH,
|
|
"autoscaler/aws/example-minimal.yaml")
|
|
with open(aws_config_path) as f:
|
|
config = yaml.safe_load(f)
|
|
|
|
# Test validate security group with custom permissions
|
|
ip_permissions = [{
|
|
"FromPort": port,
|
|
"ToPort": port,
|
|
"IpProtocol": "TCP",
|
|
"IpRanges": [{
|
|
"CidrIp": "0.0.0.0/0"
|
|
}],
|
|
} for port in [80, 443, 8265]]
|
|
config["provider"].update({
|
|
"security_group": {
|
|
"IpPermissions": ip_permissions
|
|
}
|
|
})
|
|
config = prepare_config(copy.deepcopy(config))
|
|
try:
|
|
validate_config(config)
|
|
assert config["provider"]["security_group"][
|
|
"IpPermissions"] == ip_permissions
|
|
except Exception:
|
|
self.fail(
|
|
"Failed to validate config with security group in bound rules!"
|
|
)
|
|
|
|
# Test validate security group with custom name
|
|
group_name = "test_security_group_name"
|
|
config["provider"]["security_group"].update({"GroupName": group_name})
|
|
|
|
try:
|
|
validate_config(config)
|
|
assert config["provider"]["security_group"][
|
|
"GroupName"] == group_name
|
|
except Exception:
|
|
self.fail("Failed to validate config with security group name!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import pytest
|
|
import sys
|
|
sys.exit(pytest.main(["-v", __file__]))
|