From cb419a9ad7f4c5cd8b49db98ce1a4b0a89e40385 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Thu, 28 Feb 2019 15:43:13 +0200 Subject: [PATCH] Run CLI on Windows (#548) --- appveyor.yml | 29 ++++++++ python/Makefile | 13 +++- python/neuromation/cli/command_handlers.py | 18 ++--- python/neuromation/cli/const.py | 5 ++ python/neuromation/cli/main.py | 5 ++ python/neuromation/cli/rc.py | 3 +- python/neuromation/cli/storage.py | 9 +-- python/neuromation/client/storage.py | 9 +-- python/neuromation/client/url_utils.py | 19 ++++- python/neuromation/utils.py | 2 +- python/pytest.ini | 2 +- python/tests/cli/test_rc.py | 2 + python/tests/client/test_images.py | 4 ++ python/tests/client/test_url_utils.py | 77 +++++++++------------ python/tests/e2e/conftest.py | 23 ++++-- python/tests/e2e/test_e2e.py | 5 +- python/tests/e2e/test_e2e_copy_recursive.py | 5 +- python/tests/e2e/test_e2e_images.py | 7 ++ python/tests/e2e/test_e2e_jobs.py | 8 +++ python/tests/e2e/test_e2e_network.py | 3 + 20 files changed, 173 insertions(+), 75 deletions(-) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..39a7175c1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +image: Visual Studio 2015 + +environment: + # ensure Python 3.6 is first in path + PATH: 'C:\Python37;C:\Python37\Scripts;%PATH%' + # GNU make + MAKE: C:\MinGW\bin\mingw32-make.exe + CLIENT_TEST_E2E_USER_NAME: + secure: eOtqZJ4yXsPlfNYTZ17U7NFym9KFIq1Cz/zb7dNLR4IAzz4lKtNRPKpGxqgS/BQZ8Kxbsf9EZ2EDKKC74P8kNHwb4oz++CJw6DLnzN+B3x4Nqg9gaMo/VB5yhlbxn90NPdhUl/m8j8pW0kWtwgah6U/Sh/7ZoIHycX2BgcbEONZjyRox2g9x1oKkFUEhVU+yU36EjEwrtu3Imft0yWLcXw== + +platform: x64 + +install: + # install dev dependencies + - '%MAKE% -C python init' + +build: off + +# do not run automatic test discovery +test: off + +test_script: + - '%MAKE% -C python test' + - '%MAKE% -C python _e2e_win' + - '%MAKE% -C python coverage' + +# will start RDP server you can connect to for remote debugging (a-la ssh in circleci) +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/python/Makefile b/python/Makefile index fbf13ba3f..de48283e5 100644 --- a/python/Makefile +++ b/python/Makefile @@ -90,6 +90,16 @@ _e2e: --verbose \ tests +.PHONY: _e2e_win +_e2e_win: + pytest \ + -m "e2e and not no_win32" \ + --cov=neuromation \ + --cov-report term-missing:skip-covered \ + --cov-report xml:coverage.xml \ + --cov-append \ + tests + .PHONY: test test: @@ -155,7 +165,8 @@ publish: dist publish-lint .PHONY: coverage coverage: - $(SHELL) <(curl -s https://codecov.io/bash) -X coveragepy + pip install codecov + codecov -f coverage.xml -X gcov .PHONY: format format: diff --git a/python/neuromation/cli/command_handlers.py b/python/neuromation/cli/command_handlers.py index 15c58a881..0113ca401 100644 --- a/python/neuromation/cli/command_handlers.py +++ b/python/neuromation/cli/command_handlers.py @@ -1,6 +1,6 @@ import logging import os -from pathlib import PosixPath, PurePosixPath +from pathlib import Path from urllib.parse import ParseResult, urlparse @@ -37,22 +37,22 @@ def _is_storage_path_url(self, path: ParseResult) -> None: if path.scheme != "storage": raise ValueError("Path should be targeting platform storage.") - def _render_platform_path(self, path_str: str) -> PosixPath: - target_path: PosixPath = PosixPath(path_str) + def _render_platform_path(self, path_str: str) -> Path: + target_path: Path = Path(path_str) if target_path.is_absolute(): - target_path = target_path.relative_to(PosixPath("/")) + target_path = target_path.relative_to(Path("/")) return target_path - def _render_platform_path_with_principal(self, path: ParseResult) -> PurePosixPath: - target_path: PosixPath = self._render_platform_path(path.path) + def _render_platform_path_with_principal(self, path: ParseResult) -> Path: + target_path: Path = self._render_platform_path(path.path) target_principal = self._get_principal(path) - posix_path = PurePosixPath(PLATFORM_DELIMITER, target_principal, target_path) + posix_path = Path(PLATFORM_DELIMITER, target_principal, target_path) return posix_path - def render_uri_path_with_principal(self, path: str) -> PurePosixPath: + def render_uri_path_with_principal(self, path: str) -> Path: # Special case that shall be handled here, when path is '//' if path == "storage://": - return PosixPath(PLATFORM_DELIMITER) + return Path(PLATFORM_DELIMITER) # Normal processing flow path_url = urlparse(path, scheme="file") diff --git a/python/neuromation/cli/const.py b/python/neuromation/cli/const.py index 1a14bcf1d..50a51a331 100644 --- a/python/neuromation/cli/const.py +++ b/python/neuromation/cli/const.py @@ -1,3 +1,8 @@ +import sys + + +WIN32 = sys.platform == "win32" + # Python on Windows doesn't expose these constants in os module EX_CANTCREAT = 73 diff --git a/python/neuromation/cli/main.py b/python/neuromation/cli/main.py index 49cec8450..afbc8d622 100644 --- a/python/neuromation/cli/main.py +++ b/python/neuromation/cli/main.py @@ -1,3 +1,4 @@ +import asyncio import logging import shutil import sys @@ -18,6 +19,10 @@ from .utils import Context, DeprecatedGroup, MainGroup, alias, format_example +if sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + + log = logging.getLogger(__name__) diff --git a/python/neuromation/cli/rc.py b/python/neuromation/cli/rc.py index 157450f89..1254b1ffe 100644 --- a/python/neuromation/cli/rc.py +++ b/python/neuromation/cli/rc.py @@ -14,6 +14,7 @@ from neuromation.client.config import get_server_config from neuromation.client.users import get_token_username +from .const import WIN32 from .defaults import API_URL from .login import AuthConfig, AuthNegotiator, AuthToken @@ -277,7 +278,7 @@ def _deserialize_auth_token(payload: Dict[str, Any]) -> Optional[AuthToken]: def _load(path: Path) -> Config: stat = path.stat() - if stat.st_mode & 0o777 != 0o600: + if not WIN32 and stat.st_mode & 0o777 != 0o600: raise RCException( f"Config file {path} has compromised permission bits, " f"run 'chmod 600 {path}' before usage" diff --git a/python/neuromation/cli/storage.py b/python/neuromation/cli/storage.py index 2da7f2d28..ddacf4182 100644 --- a/python/neuromation/cli/storage.py +++ b/python/neuromation/cli/storage.py @@ -131,10 +131,11 @@ async def cp( dst = URL(destination) progress_obj = ProgressBase.create_progress(progress) - if not src.scheme: - src = URL(f"file:{src.path}") - if not dst.scheme: - dst = URL(f"file:{dst.path}") + # len(uri.scheme) == 1 is a workaround for Windows path like C:/path/to.txt + if not src.scheme or len(src.scheme) == 1: + src = URL(f"file:{source}") + if not dst.scheme or len(dst.scheme) == 1: + dst = URL(f"file:{destination}") async with cfg.make_client(timeout=timeout) as client: if src.scheme == "file" and dst.scheme == "storage": src = normalize_local_path_uri(src) diff --git a/python/neuromation/client/storage.py b/python/neuromation/client/storage.py index 7beae8977..ec3209efa 100644 --- a/python/neuromation/client/storage.py +++ b/python/neuromation/client/storage.py @@ -8,6 +8,7 @@ from yarl import URL from neuromation.client.url_utils import ( + _extract_path, normalize_local_path_uri, normalize_storage_path_uri, ) @@ -156,7 +157,7 @@ async def _iterate_file( async def upload_file(self, progress: AbstractProgress, src: URL, dst: URL) -> None: src = normalize_local_path_uri(src) dst = normalize_storage_path_uri(dst, self._config.username) - path = Path(src.path) + path = _extract_path(src) if not path.exists(): raise FileNotFoundError(f"'{path}' does not exist") if path.is_dir(): @@ -188,7 +189,7 @@ async def upload_dir(self, progress: AbstractProgress, src: URL, dst: URL) -> No dst = dst / src.name src = normalize_local_path_uri(src) dst = normalize_storage_path_uri(dst, self._config.username) - path = Path(src.path).resolve() + path = _extract_path(src).resolve() if not path.exists(): raise FileNotFoundError(f"{path} does not exist") if not path.is_dir(): @@ -215,7 +216,7 @@ async def download_file( ) -> None: src = normalize_storage_path_uri(src, self._config.username) dst = normalize_local_path_uri(dst) - path = Path(dst.path) + path = _extract_path(dst) if path.exists(): if path.is_dir(): path = path / src.name @@ -237,7 +238,7 @@ async def download_dir( ) -> None: src = normalize_storage_path_uri(src, self._config.username) dst = normalize_local_path_uri(dst) - path = Path(dst.path) + path = _extract_path(dst) path.mkdir(parents=True, exist_ok=True) for child in await self.ls(src): if child.is_file(): diff --git a/python/neuromation/client/url_utils.py b/python/neuromation/client/url_utils.py index d47ac0c92..02f404045 100644 --- a/python/neuromation/client/url_utils.py +++ b/python/neuromation/client/url_utils.py @@ -1,3 +1,5 @@ +import re +import sys from pathlib import Path from yarl import URL @@ -17,6 +19,9 @@ def normalize_storage_path_uri(uri: URL, username: str) -> URL: uri = URL("storage://" + username + "/" + uri.path) uri = uri.with_path(uri.path.lstrip("/")) + if "~" in uri.path: + raise ValueError(f"Cannot expand user for {uri}") + return uri @@ -29,8 +34,20 @@ def normalize_local_path_uri(uri: URL) -> URL: ) if uri.host: raise ValueError(f"Host part is not allowed, found '{uri.host}'") - path = Path(uri.path).expanduser().absolute() + path = _extract_path(uri) + path = path.expanduser().absolute() ret = URL(path.as_uri()) + if "~" in ret.path: + raise ValueError(f"Cannot expand user for {uri}") while ret.path.startswith("//"): ret = ret.with_path(ret.path[1:]) return ret + + +def _extract_path(uri: URL) -> Path: + path = Path(uri.path) + if sys.platform == "win32": + # result of previous normalization + if re.match(r"^[/\\][A-Za-z]:[/\\]", str(path)): + return Path(str(path)[1:]) + return path diff --git a/python/neuromation/utils.py b/python/neuromation/utils.py index 8043df3b6..1d8bc5e84 100644 --- a/python/neuromation/utils.py +++ b/python/neuromation/utils.py @@ -63,7 +63,7 @@ async def main(): warnings.simplefilter("ignore", ResourceWarning) loop.close() del loop - gc.collect(2) + gc.collect() def _cancel_all_tasks( diff --git a/python/pytest.ini b/python/pytest.ini index 0ac4a4b71..b560fa7a6 100644 --- a/python/pytest.ini +++ b/python/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts= --cov-branch +addopts= --cov-branch --cov-report xml log_cli=false log_level=INFO filterwarnings=error diff --git a/python/tests/cli/test_rc.py b/python/tests/cli/test_rc.py index c47c5c7fd..562d01fd2 100644 --- a/python/tests/cli/test_rc.py +++ b/python/tests/cli/test_rc.py @@ -1,4 +1,5 @@ import logging +import sys from pathlib import Path from textwrap import dedent from unittest import mock @@ -334,6 +335,7 @@ def test_load_missing(nmrc): assert config == DEFAULTS +@pytest.mark.skipif(sys.platform == "win32", reason="chmod 0o600 works on Posix only") def test_load_bad_file_mode(nmrc): document = """ url: 'http://a.b/c' diff --git a/python/tests/client/test_images.py b/python/tests/client/test_images.py index d0c07fab4..b2aa1358e 100644 --- a/python/tests/client/test_images.py +++ b/python/tests/client/test_images.py @@ -1,4 +1,5 @@ import os +import sys import asynctest import pytest @@ -564,6 +565,9 @@ async def message_generator(): class TestRegistry: + @pytest.mark.skipif( + sys.platform == "win32", reason="aiodocker doens't support Windows pipes yet" + ) async def test_ls(self, aiohttp_server, token): JSON = {"repositories": ["image://bob/alpine", "image://jill/bananas"]} diff --git a/python/tests/client/test_url_utils.py b/python/tests/client/test_url_utils.py index 8c739a2ca..b99f2e0a5 100644 --- a/python/tests/client/test_url_utils.py +++ b/python/tests/client/test_url_utils.py @@ -1,3 +1,4 @@ +import sys from pathlib import Path import pytest @@ -5,6 +6,7 @@ from neuromation.client import Client from neuromation.client.url_utils import ( + _extract_path, normalize_local_path_uri, normalize_storage_path_uri, ) @@ -46,8 +48,7 @@ async def test_normalize_local_path_uri__0_slashes_relative(token, pwd): url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == f"{pwd}/path/to/file.txt" - assert str(url) == f"file://{pwd}/path/to/file.txt" + assert _extract_path(url) == pwd / "path/to/file.txt" async def test_normalize_storage_path_uri__1_slash_absolute(token, client): @@ -64,8 +65,7 @@ async def test_normalize_local_path_uri__1_slash_absolute(token, pwd): url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == "/path/to/file.txt" - assert str(url) == "file:///path/to/file.txt" + assert _extract_path(url) == Path(pwd.drive + "/path/to/file.txt") async def test_normalize_storage_path_uri__2_slashes(token, client): @@ -97,8 +97,7 @@ async def test_normalize_local_path_uri__3_slashes_relative(token, pwd): url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == "/path/to/file.txt" - assert str(url) == "file:///path/to/file.txt" + assert _extract_path(url) == Path(pwd.drive + "/path/to/file.txt") async def test_normalize_storage_path_uri__4_slashes_relative(token, client): @@ -110,22 +109,20 @@ async def test_normalize_storage_path_uri__4_slashes_relative(token, client): assert str(url) == "storage://user/path/to/file.txt" -async def test_normalize_local_path_uri__4_slashes_relative(token, pwd): +@pytest.mark.skipif(sys.platform == "win32", reason="Doesn't work on Windows") +async def test_normalize_local_path_uri__4_slashes_relative(): url = URL("file:////path/to/file.txt") url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None assert url.path == "/path/to/file.txt" - assert str(url) == "file:///path/to/file.txt" + assert str(url) == f"file:///path/to/file.txt" async def test_normalize_storage_path_uri__tilde_in_relative_path(token, client): url = URL("storage:~/path/to/file.txt") - url = normalize_storage_path_uri(url, client.username) - assert url.scheme == "storage" - assert url.host == "user" - assert url.path == "/~/path/to/file.txt" - assert str(url) == "storage://user/~/path/to/file.txt" + with pytest.raises(ValueError, match=".*Cannot expand user.*"): + normalize_storage_path_uri(url, client.username) async def test_normalize_local_path_uri__tilde_in_relative_path(token, fake_homedir): @@ -133,26 +130,20 @@ async def test_normalize_local_path_uri__tilde_in_relative_path(token, fake_home url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == f"{fake_homedir}/path/to/file.txt" - assert str(url) == f"file://{fake_homedir}/path/to/file.txt" + assert _extract_path(url) == fake_homedir / "path/to/file.txt" + assert str(url) == (fake_homedir / "path/to/file.txt").as_uri() async def test_normalize_storage_path_uri__tilde_in_absolute_path(token, client): url = URL("storage:/~/path/to/file.txt") - url = normalize_storage_path_uri(url, client.username) - assert url.scheme == "storage" - assert url.host == "user" - assert url.path == "/~/path/to/file.txt" - assert str(url) == "storage://user/~/path/to/file.txt" + with pytest.raises(ValueError, match=".*Cannot expand user.*"): + normalize_storage_path_uri(url, client.username) -async def test_normalize_local_path_uri__tilde_in_absolute_path(token): +async def test_normalize_local_path_uri__tilde_in_absolute_path(token, fake_homedir): url = URL("file:/~/path/to/file.txt") - url = normalize_local_path_uri(url) - assert url.scheme == "file" - assert url.host is None - assert url.path == "/~/path/to/file.txt" - assert str(url) == "file:///~/path/to/file.txt" + with pytest.raises(ValueError, match=".*Cannot expand user.*"): + normalize_local_path_uri(url) async def test_normalize_storage_path_uri__tilde_in_host(token, client): @@ -187,7 +178,6 @@ async def test_normalize_local_path_uri__bad_scheme(token): async def test_normalize_storage_path_uri__no_slash__double(token, client): url = URL("storage:path/to/file.txt") url = normalize_storage_path_uri(url, client.username) - url = normalize_storage_path_uri(url, client.username) assert url.scheme == "storage" assert url.host == "user" assert url.path == "/path/to/file.txt" @@ -197,48 +187,45 @@ async def test_normalize_storage_path_uri__no_slash__double(token, client): async def test_normalize_local_path_uri__no_slash__double(token, pwd): url = URL("file:path/to/file.txt") url = normalize_local_path_uri(url) - url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == f"{pwd}/path/to/file.txt" - assert str(url) == f"file://{pwd}/path/to/file.txt" + assert _extract_path(url) == pwd / "path/to/file.txt" async def test_normalize_storage_path_uri__tilde_slash__double(token, client): url = URL("storage:~/path/to/file.txt") - url = normalize_storage_path_uri(url, client.username) - url = normalize_storage_path_uri(url, client.username) - assert url.scheme == "storage" - assert url.host == "user" - assert url.path == "/~/path/to/file.txt" - assert str(url) == "storage://user/~/path/to/file.txt" + with pytest.raises(ValueError, match=".*Cannot expand user.*"): + normalize_storage_path_uri(url, client.username) async def test_normalize_local_path_uri__tilde_slash__double(token, fake_homedir): url = URL("file:~/path/to/file.txt") url = normalize_local_path_uri(url) - url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == f"{fake_homedir}/path/to/file.txt" - assert str(url) == f"file://{fake_homedir}/path/to/file.txt" + assert _extract_path(url) == fake_homedir / "path/to/file.txt" + assert str(url) == (fake_homedir / "path/to/file.txt").as_uri() async def test_normalize_storage_path_uri__3_slashes__double(token, client): url = URL("storage:///path/to/file.txt") url = normalize_storage_path_uri(url, client.username) - url = normalize_storage_path_uri(url, client.username) assert url.scheme == "storage" assert url.host == "user" assert url.path == "/path/to/file.txt" assert str(url) == "storage://user/path/to/file.txt" -async def test_normalize_local_path_uri__3_slashes__double(token, fake_homedir): - url = URL("file:///path/to/file.txt") - url = normalize_local_path_uri(url) +async def test_normalize_local_path_uri__3_slashes__double(token, pwd): + url = URL(f"file:///{pwd}/path/to/file.txt") url = normalize_local_path_uri(url) assert url.scheme == "file" assert url.host is None - assert url.path == "/path/to/file.txt" - assert str(url) == "file:///path/to/file.txt" + assert _extract_path(url) == pwd / "path/to/file.txt" + assert str(url) == (pwd / "path/to/file.txt").as_uri() + + +@pytest.mark.skipif(sys.platform != "win32", reason="Requires Windows") +def test_normalized_path(): + p = URL("file:///Z:/neuromation/platform-api-clients/python/setup.py") + assert normalize_local_path_uri(p) == p diff --git a/python/tests/e2e/conftest.py b/python/tests/e2e/conftest.py index dfc737987..73d61e459 100644 --- a/python/tests/e2e/conftest.py +++ b/python/tests/e2e/conftest.py @@ -3,6 +3,7 @@ import logging import os import re +import sys import tempfile from collections import namedtuple from contextlib import suppress @@ -55,9 +56,21 @@ class TestRetriesExceeded(Exception): SysCap = namedtuple("SysCap", "out err") +async def _run_async(coro, *args, **kwargs): + try: + return await coro(*args, **kwargs) + finally: + if sys.platform == "win32": + await asyncio.sleep(0.2) + else: + await asyncio.sleep(0.05) + + def run_async(coro): def wrapper(*args, **kwargs): - return run(coro(*args, **kwargs)) + if sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + return run(_run_async(coro, *args, **kwargs)) return wrapper @@ -332,10 +345,12 @@ async def _check_job_output(): def run_cli(self, arguments: List[str], storage_retry: bool = True) -> SysCap: def _temp_config(): - config_file = tempfile.NamedTemporaryFile( + with tempfile.NamedTemporaryFile( dir=self._tmp, prefix="run_cli-", suffix=".nmrc", delete=False - ) - config_path = Path(config_file.name) + ) as config_file: + # close tmp file on exit from context manager, + # it prevents unlink() error on Windows + config_path = Path(config_file.name) rc.save(config_path, self._config) return config_path diff --git a/python/tests/e2e/test_e2e.py b/python/tests/e2e/test_e2e.py index 9097608e0..40dd769fd 100644 --- a/python/tests/e2e/test_e2e.py +++ b/python/tests/e2e/test_e2e.py @@ -43,11 +43,10 @@ def test_empty_directory_ls_output(helper): # Ensure output of ls - empty directory shall print nothing. captured = helper.run_cli(["storage", "ls", helper.tmpstorage]) assert not captured.out - # FIXME: stderr has "Using path ..." line - assert len(captured.err.splitlines()) == 1 and captured.err.startswith("Using path") @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_job_top(helper): def split_non_empty_parts(line, separator=None): return [part.strip() for part in line.split(separator) if part.strip()] @@ -101,6 +100,7 @@ def split_non_empty_parts(line, separator=None): @pytest.mark.e2e +@pytest.mark.no_win32 @pytest.mark.parametrize( "switch,expected", [["--extshm", True], ["--no-extshm", False], [None, True]], # default is enabled @@ -162,6 +162,7 @@ def test_e2e_storage(data, tmp_path, helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_job_storage_interaction(helper, data, tmp_path): srcfile, checksum = data # Create directory for the test diff --git a/python/tests/e2e/test_e2e_copy_recursive.py b/python/tests/e2e/test_e2e_copy_recursive.py index 9533626cf..98643e6b2 100644 --- a/python/tests/e2e/test_e2e_copy_recursive.py +++ b/python/tests/e2e/test_e2e_copy_recursive.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from tests.e2e.utils import FILE_SIZE_B @@ -6,7 +8,7 @@ @pytest.mark.e2e def test_e2e_copy_recursive_to_platform(helper, nested_data, tmp_path): srcfile, checksum, dir_path = nested_data - target_file_name = srcfile.split("/")[-1] + target_file_name = Path(srcfile).name # Upload local file captured = helper.run_cli(["storage", "cp", "-r", dir_path, helper.tmpstorage]) @@ -19,7 +21,6 @@ def test_e2e_copy_recursive_to_platform(helper, nested_data, tmp_path): ) # Download into local directory and confirm checksum - targetdir = tmp_path / "bar" targetdir.mkdir() helper.run_cli(["storage", "cp", "-r", f"{helper.tmpstorage}", str(targetdir)]) diff --git a/python/tests/e2e/test_e2e_images.py b/python/tests/e2e/test_e2e_images.py index 331890a79..39f477809 100644 --- a/python/tests/e2e/test_e2e_images.py +++ b/python/tests/e2e/test_e2e_images.py @@ -1,4 +1,5 @@ import re +import sys from pathlib import Path from uuid import uuid4 as uuid @@ -54,6 +55,9 @@ async def image(loop, docker, tag): @pytest.mark.e2e +@pytest.mark.skipif( + sys.platform == "win32", reason="Image operations are not supported on Windows yet" +) def test_images_complete_lifecycle(helper, image, tag, loop, docker): # Let`s push image captured = helper.run_cli(["image", "push", image]) @@ -112,6 +116,9 @@ def test_images_complete_lifecycle(helper, image, tag, loop, docker): @pytest.mark.e2e +@pytest.mark.skipif( + sys.platform == "win32", reason="Image operations are not supported on Windows yet" +) def test_images_push_with_specified_name(helper, image, tag, loop, docker): # Let`s push image image_no_tag = image.replace(f":{tag}", "") diff --git a/python/tests/e2e/test_e2e_jobs.py b/python/tests/e2e/test_e2e_jobs.py index 956771928..48e4313d7 100644 --- a/python/tests/e2e/test_e2e_jobs.py +++ b/python/tests/e2e/test_e2e_jobs.py @@ -166,6 +166,7 @@ def test_job_description(helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_unschedulable_job_lifecycle(helper): # Remember original running jobs captured = helper.run_cli( @@ -231,6 +232,7 @@ def test_unschedulable_job_lifecycle(helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_two_jobs_at_once(helper): # Remember original running jobs captured = helper.run_cli( @@ -396,6 +398,7 @@ async def get_(url): @pytest.mark.e2e +@pytest.mark.no_win32 def test_model_without_command(helper): loop_sleep = 1 service_wait_time = 60 @@ -451,6 +454,7 @@ async def get_(url): @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_no_env(helper): bash_script = 'echo "begin"$VAR"end" | grep beginend' command = f"bash -c '{bash_script}'" @@ -546,6 +550,7 @@ def test_e2e_env_from_local(helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_multiple_env(helper): bash_script = 'echo begin"$VAR""$VAR2"end | grep beginVALVAL2end' command = f"bash -c '{bash_script}'" @@ -619,6 +624,7 @@ def test_e2e_multiple_env_from_file(helper, tmp_path): @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_ssh_exec_true(helper): command = 'bash -c "sleep 15m; false"' captured = helper.run_cli( @@ -672,6 +678,7 @@ def test_e2e_ssh_exec_false(helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_ssh_exec_no_cmd(helper): command = 'bash -c "sleep 15m; false"' captured = helper.run_cli( @@ -815,6 +822,7 @@ def test_e2e_ssh_exec_dead_job(helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_e2e_job_list_filtered_by_status(helper): N_JOBS = 5 diff --git a/python/tests/e2e/test_e2e_network.py b/python/tests/e2e/test_e2e_network.py index 44a013ff3..519ccee16 100644 --- a/python/tests/e2e/test_e2e_network.py +++ b/python/tests/e2e/test_e2e_network.py @@ -42,6 +42,7 @@ def go(http_port: bool): @pytest.mark.e2e +@pytest.mark.no_win32 def test_connectivity_job_with_http_port(secret_job, check_http_get, helper): http_job = secret_job(True) @@ -76,6 +77,7 @@ def test_connectivity_job_with_http_port(secret_job, check_http_get, helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_connectivity_job_without_http_port(secret_job, check_http_get, helper): # run http job for getting url http_job = secret_job(True) @@ -114,6 +116,7 @@ def test_connectivity_job_without_http_port(secret_job, check_http_get, helper): @pytest.mark.e2e +@pytest.mark.no_win32 def test_check_isolation(secret_job, helper_alt): http_job = secret_job(True)