211 lines
5.4 KiB
Python
211 lines
5.4 KiB
Python
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import pytest
|
|
import requests
|
|
|
|
import ray
|
|
from ray._common.test_utils import (
|
|
run_string_as_driver,
|
|
wait_for_condition,
|
|
)
|
|
from ray._private import ray_constants
|
|
|
|
import psutil
|
|
|
|
|
|
def search_agents(cluster):
|
|
all_processes = cluster.head_node.all_processes
|
|
raylet_proc_info = all_processes[ray_constants.PROCESS_TYPE_RAYLET][0]
|
|
raylet_proc = psutil.Process(raylet_proc_info.process.pid)
|
|
|
|
def _search_agent(processes):
|
|
for p in processes:
|
|
try:
|
|
for c in p.cmdline():
|
|
if ray_constants.AGENT_PROCESS_TYPE_DASHBOARD_AGENT[:15] in c:
|
|
return p
|
|
except Exception:
|
|
pass
|
|
|
|
agent_proc = _search_agent(raylet_proc.children())
|
|
return agent_proc
|
|
|
|
|
|
def test_ray_start_default_port_conflict(call_ray_stop_only, shutdown_only):
|
|
subprocess.check_call(["ray", "start", "--head"])
|
|
ray.init(address="auto")
|
|
assert (
|
|
str(ray_constants.DEFAULT_DASHBOARD_PORT)
|
|
in ray._private.worker.get_dashboard_url()
|
|
)
|
|
|
|
error_raised = False
|
|
try:
|
|
subprocess.check_output(
|
|
[
|
|
"ray",
|
|
"start",
|
|
"--head",
|
|
"--port",
|
|
"9999", # use a different gcs port
|
|
"--include-dashboard=True",
|
|
],
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
except subprocess.CalledProcessError as e:
|
|
assert b"already occupied" in e.stderr
|
|
error_raised = True
|
|
|
|
assert error_raised, "ray start should cause a conflict error"
|
|
|
|
|
|
def test_port_auto_increment(shutdown_only):
|
|
ray.init()
|
|
url = ray._private.worker.get_dashboard_url()
|
|
|
|
def dashboard_available():
|
|
try:
|
|
requests.get("http://" + url).status_code == 200
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
wait_for_condition(dashboard_available)
|
|
|
|
run_string_as_driver(
|
|
f"""
|
|
import ray
|
|
from ray._common.test_utils import wait_for_condition
|
|
import requests
|
|
ray.init()
|
|
url = ray._private.worker.get_dashboard_url()
|
|
assert url != "{url}"
|
|
def dashboard_available():
|
|
try:
|
|
requests.get("http://"+url).status_code == 200
|
|
return True
|
|
except:
|
|
return False
|
|
wait_for_condition(dashboard_available)
|
|
ray.shutdown()
|
|
"""
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"listen_port",
|
|
[9999],
|
|
indirect=True,
|
|
)
|
|
def test_port_conflict(listen_port, call_ray_stop_only, shutdown_only):
|
|
try:
|
|
subprocess.check_output(
|
|
[
|
|
"ray",
|
|
"start",
|
|
"--head",
|
|
"--port",
|
|
"9989",
|
|
"--dashboard-port",
|
|
"9999",
|
|
"--include-dashboard=True",
|
|
],
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
except subprocess.CalledProcessError as e:
|
|
assert b"already occupied" in e.stderr
|
|
|
|
with pytest.raises(ValueError, match="already occupied"):
|
|
ray.init(dashboard_port=9999, include_dashboard=True)
|
|
|
|
|
|
def test_dashboard(shutdown_only):
|
|
addresses = ray.init(include_dashboard=True, num_cpus=1)
|
|
dashboard_url = addresses["webui_url"]
|
|
assert ray._private.worker.get_dashboard_url() == dashboard_url
|
|
|
|
assert re.match(r"^(localhost|\d+\.\d+\.\d+\.\d+):\d+$", dashboard_url)
|
|
|
|
start_time = time.time()
|
|
while True:
|
|
try:
|
|
node_info_url = f"http://{dashboard_url}/nodes"
|
|
resp = requests.get(node_info_url, params={"view": "summary"})
|
|
resp.raise_for_status()
|
|
summaries = resp.json()
|
|
assert summaries["result"] is True
|
|
assert "msg" in summaries
|
|
break
|
|
except (requests.exceptions.ConnectionError, AssertionError):
|
|
if time.time() > start_time + 30:
|
|
out_log = None
|
|
with open(
|
|
"{}/logs/dashboard.log".format(addresses["session_dir"]), "r"
|
|
) as f:
|
|
out_log = f.read()
|
|
raise Exception(
|
|
"Timed out while waiting for dashboard to start. "
|
|
f"Dashboard output log: {out_log}\n"
|
|
)
|
|
|
|
|
|
conflict_port = 34567
|
|
configured_test_port = 34568
|
|
|
|
|
|
def run_tasks_without_runtime_env():
|
|
assert ray.is_initialized()
|
|
|
|
@ray.remote
|
|
def f():
|
|
pass
|
|
|
|
for _ in range(10):
|
|
time.sleep(1)
|
|
ray.get(f.remote())
|
|
|
|
|
|
def run_tasks_with_runtime_env():
|
|
assert ray.is_initialized()
|
|
|
|
@ray.remote(runtime_env={"pip": ["pip-install-test==0.5"]})
|
|
def f():
|
|
import pip_install_test # noqa
|
|
|
|
pass
|
|
|
|
for _ in range(3):
|
|
time.sleep(1)
|
|
ray.get(f.remote())
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform == "win32", reason="`runtime_env` with `pip` not supported on Windows."
|
|
)
|
|
@pytest.mark.parametrize(
|
|
"listen_port",
|
|
[conflict_port],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.parametrize(
|
|
"call_ray_start",
|
|
[
|
|
f"ray start --head --num-cpus=1 --metrics-export-port={conflict_port}",
|
|
f"ray start --head --num-cpus=1 --dashboard-agent-listen-port={conflict_port}",
|
|
],
|
|
indirect=True,
|
|
)
|
|
def test_dashboard_agent_metrics_or_http_port_conflict(listen_port, call_ray_start):
|
|
address = call_ray_start
|
|
ray.init(address=address)
|
|
# Tasks with runtime env still work when other agent port conflicts,
|
|
# except grpc port.
|
|
run_tasks_with_runtime_env()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main(["-sv", __file__]))
|