Skip to content

Commit

Permalink
[ci] os-specific tester container (ray-project#41635)
Browse files Browse the repository at this point in the history
Create Linux and Windows tester container. Test via unit tests. Will run e2e test on the top of this stack.

Signed-off-by: can <can@anyscale.com>
  • Loading branch information
can-anyscale authored Dec 8, 2023
1 parent 959a48b commit 4eb27f6
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 35 deletions.
19 changes: 17 additions & 2 deletions ci/ray_ci/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,24 @@ py_test(
)

py_test(
name = "test_tester_container",
name = "test_linux_tester_container",
size = "small",
srcs = ["test_tester_container.py"],
srcs = ["test_linux_tester_container.py"],
tags = [
"ci_unit",
"team:ci",
],
deps = [
":ray_ci_lib",
":ray_ci_lib_test",
ci_require("pytest"),
],
)

py_test(
name = "test_windows_tester_container",
size = "small",
srcs = ["test_windows_tester_container.py"],
tags = [
"ci_unit",
"team:ci",
Expand Down
36 changes: 36 additions & 0 deletions ci/ray_ci/linux_tester_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
from typing import List, Optional

from ci.ray_ci.linux_container import LinuxContainer
from ci.ray_ci.tester_container import TesterContainer


class LinuxTesterContainer(TesterContainer, LinuxContainer):
def __init__(
self,
docker_tag: str,
shard_count: int = 1,
gpus: int = 0,
test_envs: Optional[List[str]] = None,
shard_ids: Optional[List[int]] = None,
skip_ray_installation: bool = False,
build_type: Optional[str] = None,
) -> None:
LinuxContainer.__init__(
self,
docker_tag,
envs=test_envs,
volumes=[
f"{os.environ.get('RAYCI_CHECKOUT_DIR')}:/ray-mount",
"/var/run/docker.sock:/var/run/docker.sock",
],
)
TesterContainer.__init__(
self,
shard_count,
gpus,
test_envs=test_envs,
shard_ids=shard_ids,
skip_ray_installation=skip_ray_installation,
build_type=build_type,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from unittest import mock
from typing import List, Optional

from ci.ray_ci.tester_container import TesterContainer
from ci.ray_ci.linux_tester_container import LinuxTesterContainer
from ci.ray_ci.utils import chunk_into_n
from ci.ray_ci.container import _DOCKER_ECR_REPO, _RAYCI_BUILD_ID

Expand All @@ -24,15 +24,15 @@ def wait(self) -> int:
def test_enough_gpus() -> None:
# not enough gpus
try:
TesterContainer("team", shard_count=2, gpus=1, skip_ray_installation=True)
LinuxTesterContainer("team", shard_count=2, gpus=1, skip_ray_installation=True)
except AssertionError:
pass
else:
assert False, "Should raise an AssertionError"

# not enough gpus
try:
TesterContainer("team", shard_count=1, gpus=1, skip_ray_installation=True)
LinuxTesterContainer("team", shard_count=1, gpus=1, skip_ray_installation=True)
except AssertionError:
assert False, "Should not raise an AssertionError"

Expand All @@ -44,10 +44,10 @@ def _mock_popen(input: List[str]) -> None:
inputs.append(" ".join(input))

with mock.patch("subprocess.Popen", side_effect=_mock_popen), mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
return_value=None,
):
TesterContainer(
LinuxTesterContainer(
"team", build_type="debug", test_envs=["ENV_01", "ENV_02"]
)._run_tests_in_docker(["t1", "t2"], [0, 1], ["v=k"], "flag")
input_str = inputs[-1]
Expand All @@ -58,7 +58,7 @@ def _mock_popen(input: List[str]) -> None:
"--config=ci-debug --test_env v=k --test_arg flag t1 t2" in input_str
)

TesterContainer("team")._run_tests_in_docker(["t1", "t2"], [], ["v=k"])
LinuxTesterContainer("team")._run_tests_in_docker(["t1", "t2"], [], ["v=k"])
input_str = inputs[-1]
assert "--env BUILDKITE_BUILD_URL" in input_str
assert "--gpus" not in input_str
Expand All @@ -72,10 +72,10 @@ def _mock_check_output(input: List[str]) -> None:
with mock.patch(
"subprocess.check_output", side_effect=_mock_check_output
), mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
return_value=None,
):
container = TesterContainer("team")
container = LinuxTesterContainer("team")
container.run_script_with_output(["run command"])


Expand All @@ -86,13 +86,13 @@ def _mock_install_ray(build_type: Optional[str]) -> None:
install_ray_called.append(True)

with mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
side_effect=_mock_install_ray,
):
assert len(install_ray_called) == 0
TesterContainer("team", skip_ray_installation=False)
LinuxTesterContainer("team", skip_ray_installation=False)
assert len(install_ray_called) == 1
TesterContainer("team", skip_ray_installation=True)
LinuxTesterContainer("team", skip_ray_installation=True)
assert len(install_ray_called) == 1


Expand All @@ -103,7 +103,7 @@ def _mock_subprocess(inputs: List[str], env, stdout, stderr) -> None:
install_ray_cmds.append(inputs)

with mock.patch("subprocess.check_call", side_effect=_mock_subprocess):
TesterContainer("team", build_type="debug")
LinuxTesterContainer("team", build_type="debug")
docker_image = f"{_DOCKER_ECR_REPO}:{_RAYCI_BUILD_ID}-team"
assert install_ray_cmds[-1] == [
"docker",
Expand Down Expand Up @@ -134,15 +134,15 @@ def _mock_shard_tests(tests: List[str], workers: int, worker_id: int) -> List[st
return chunk_into_n(tests, workers)[worker_id]

with mock.patch(
"ci.ray_ci.tester_container.TesterContainer._run_tests_in_docker",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer._run_tests_in_docker",
side_effect=_mock_run_tests_in_docker,
), mock.patch(
"ci.ray_ci.tester_container.shard_tests", side_effect=_mock_shard_tests
), mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
return_value=None,
):
container = TesterContainer("team", shard_count=2, shard_ids=[0, 1])
container = LinuxTesterContainer("team", shard_count=2, shard_ids=[0, 1])
# test_targets are not empty
assert container.run_tests(["t1", "t2"], [])
# test_targets is empty after chunking, but not creating popen
Expand Down
10 changes: 5 additions & 5 deletions ci/ray_ci/test_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest

from ci.ray_ci.tester_container import TesterContainer
from ci.ray_ci.linux_tester_container import LinuxTesterContainer
from ci.ray_ci.tester import (
_add_default_except_tags,
_get_container,
Expand All @@ -32,7 +32,7 @@ def test_get_tag_matcher() -> None:

def test_get_container() -> None:
with mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
return_value=None,
):
container = _get_container("core", 3, 1, 2, 0)
Expand All @@ -58,12 +58,12 @@ def test_get_test_targets() -> None:
"subprocess.check_output",
return_value="\n".join(test_targets).encode("utf-8"),
), mock.patch(
"ci.ray_ci.tester_container.TesterContainer.install_ray",
"ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
return_value=None,
):
assert set(
_get_test_targets(
TesterContainer("core"),
LinuxTesterContainer("core"),
"targets",
"core",
yaml_dir=tmp,
Expand All @@ -75,7 +75,7 @@ def test_get_test_targets() -> None:
}

assert _get_test_targets(
TesterContainer("core"),
LinuxTesterContainer("core"),
"targets",
"core",
yaml_dir=tmp,
Expand Down
23 changes: 23 additions & 0 deletions ci/ray_ci/test_windows_tester_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from unittest import mock
from typing import List

from ci.ray_ci.windows_tester_container import WindowsTesterContainer


def test_init() -> None:
install_ray_cmds = []

def _mock_subprocess(inputs: List[str], stdout, stderr) -> None:
install_ray_cmds.append(inputs)

with mock.patch("subprocess.check_call", side_effect=_mock_subprocess):
WindowsTesterContainer("hi")
assert install_ray_cmds[-1] == [
"docker",
"build",
"-t",
"029272617770.dkr.ecr.us-west-2.amazonaws.com/rayproject/citemp:unknown-hi",
"-f",
"c:\\workdir\\ci\\ray_ci\\windows\\tests.env.Dockerfile",
"c:\\workdir",
]
3 changes: 2 additions & 1 deletion ci/ray_ci/tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
DEFAULT_PYTHON_VERSION,
DEFAULT_ARCHITECTURE,
)
from ci.ray_ci.linux_tester_container import LinuxTesterContainer
from ci.ray_ci.tester_container import TesterContainer
from ci.ray_ci.utils import docker_login

Expand Down Expand Up @@ -207,7 +208,7 @@ def _get_container(
shard_start = worker_id * parallelism_per_worker
shard_end = (worker_id + 1) * parallelism_per_worker

return TesterContainer(
return LinuxTesterContainer(
build_name or f"{team}build",
test_envs=test_env,
shard_count=shard_count,
Expand Down
14 changes: 2 additions & 12 deletions ci/ray_ci/tester_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
from typing import List, Optional

from ci.ray_ci.utils import shard_tests, chunk_into_n
from ci.ray_ci.linux_container import LinuxContainer
from ci.ray_ci.utils import logger
from ci.ray_ci.container import Container


class TesterContainer(LinuxContainer):
class TesterContainer(Container):
"""
A wrapper for running tests in ray ci docker container
"""

def __init__(
self,
docker_tag: str,
shard_count: int = 1,
gpus: int = 0,
test_envs: Optional[List[str]] = None,
Expand All @@ -23,20 +22,11 @@ def __init__(
build_type: Optional[str] = None,
) -> None:
"""
:param docker_tag: Name of the wanda build to be used as test container.
:param gpu: Number of gpus to use in the container. If 0, used all gpus.
:param shard_count: The number of shards to split the tests into. This can be
used to run tests in a distributed fashion.
:param shard_ids: The list of shard ids to run. If none, run no shards.
"""
super().__init__(
docker_tag,
envs=test_envs,
volumes=[
f"{os.environ.get('RAYCI_CHECKOUT_DIR')}:/ray-mount",
"/var/run/docker.sock:/var/run/docker.sock",
],
)
self.shard_count = shard_count
self.shard_ids = shard_ids or []
self.test_envs = test_envs or []
Expand Down
25 changes: 25 additions & 0 deletions ci/ray_ci/windows_tester_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import List, Optional

from ci.ray_ci.windows_container import WindowsContainer
from ci.ray_ci.tester_container import TesterContainer


class WindowsTesterContainer(TesterContainer, WindowsContainer):
def __init__(
self,
docker_tag: str,
shard_count: int = 1,
test_envs: Optional[List[str]] = None,
shard_ids: Optional[List[int]] = None,
skip_ray_installation: bool = False,
) -> None:
WindowsContainer.__init__(self, docker_tag)
TesterContainer.__init__(
self,
shard_count,
gpus=0, # We don't support GPU tests on Windows yet.
test_envs=test_envs,
shard_ids=shard_ids,
skip_ray_installation=skip_ray_installation,
build_type=None,
)

0 comments on commit 4eb27f6

Please sign in to comment.