Coverage for mlos_bench/mlos_bench/tests/services/remote/ssh/fixtures.py: 98%

45 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 00:51 +0000

1# 

2# Copyright (c) Microsoft Corporation. 

3# Licensed under the MIT License. 

4# 

5""" 

6Fixtures for the SSH service tests. 

7 

8Note: these are not in the conftest.py file because they are also used by remote_ssh_env_test.py 

9""" 

10 

11import os 

12import tempfile 

13from collections.abc import Generator 

14from subprocess import run 

15 

16import pytest 

17from pytest_docker.plugin import Services as DockerServices 

18 

19from mlos_bench.services.remote.ssh.ssh_fileshare import SshFileShareService 

20from mlos_bench.services.remote.ssh.ssh_host_service import SshHostService 

21from mlos_bench.tests import wait_docker_service_socket 

22from mlos_bench.tests.services.remote.ssh import ( 

23 ALT_TEST_SERVER_NAME, 

24 REBOOT_TEST_SERVER_NAME, 

25 SSH_TEST_SERVER_NAME, 

26 SshTestServerInfo, 

27) 

28 

29# pylint: disable=redefined-outer-name 

30 

31 

32@pytest.fixture(scope="session") 

33def docker_compose_file(pytestconfig: pytest.Config) -> list[str]: 

34 """ 

35 Fixture for the path to the docker-compose file. 

36 

37 Parameters 

38 ---------- 

39 pytestconfig : pytest.Config 

40 

41 Returns 

42 ------- 

43 list[str] 

44 List of paths to docker-compose files. 

45 """ 

46 _ = pytestconfig # unused 

47 return [ 

48 os.path.join(os.path.dirname(__file__), "docker-compose.yml"), 

49 ] 

50 

51 

52@pytest.fixture(scope="session") 

53def docker_compose_project_name(short_testrun_uid: str) -> str: 

54 """ 

55 Returns the name of the docker-compose project. 

56 

57 Returns 

58 ------- 

59 str 

60 Name of the docker-compose project. 

61 """ 

62 # Use the xdist testrun UID to ensure that the docker-compose project name 

63 # is unique across sessions, but shared amongst workers. 

64 return f"""mlos_bench-test-{short_testrun_uid}-{__name__.replace(".", "-")}""" 

65 

66 

67@pytest.fixture(scope="session") 

68def ssh_test_server( 

69 docker_hostname: str, 

70 docker_compose_project_name: str, 

71 locked_docker_services: DockerServices, 

72) -> Generator[SshTestServerInfo]: 

73 """ 

74 Fixture for getting the ssh test server services setup via docker-compose using 

75 pytest-docker. 

76 

77 Yields the (hostname, port, username, id_rsa_path) of the test server. 

78 

79 Once the session is over, the docker containers are torn down, and the temporary 

80 file holding the dynamically generated private key of the test server is deleted. 

81 """ 

82 # Get a copy of the ssh id_rsa key from the test ssh server. 

83 with tempfile.NamedTemporaryFile() as id_rsa_file: 

84 ssh_test_server_info = SshTestServerInfo( 

85 compose_project_name=docker_compose_project_name, 

86 service_name=SSH_TEST_SERVER_NAME, 

87 hostname=docker_hostname, 

88 username="root", 

89 id_rsa_path=id_rsa_file.name, 

90 ) 

91 wait_docker_service_socket( 

92 locked_docker_services, 

93 ssh_test_server_info.hostname, 

94 ssh_test_server_info.get_port(), 

95 ) 

96 id_rsa_src = f"/{ssh_test_server_info.username}/.ssh/id_rsa" 

97 docker_cp_cmd = ( 

98 f"docker compose -p {docker_compose_project_name} " 

99 f"cp {SSH_TEST_SERVER_NAME}:{id_rsa_src} {id_rsa_file.name}" 

100 ) 

101 cmd = run( 

102 docker_cp_cmd.split(), 

103 check=True, 

104 cwd=os.path.dirname(__file__), 

105 capture_output=True, 

106 text=True, 

107 ) 

108 if cmd.returncode != 0: 

109 raise RuntimeError( 

110 f"Failed to copy ssh key from {SSH_TEST_SERVER_NAME} container " 

111 + f"[return={cmd.returncode}]: {str(cmd.stderr)}" 

112 ) 

113 os.chmod(id_rsa_file.name, 0o600) 

114 yield ssh_test_server_info 

115 # NamedTempFile deleted on context exit 

116 

117 

118@pytest.fixture(scope="session") 

119def alt_test_server( 

120 ssh_test_server: SshTestServerInfo, 

121 locked_docker_services: DockerServices, 

122) -> SshTestServerInfo: 

123 """ 

124 Fixture for getting the second ssh test server info from the docker-compose.yml. 

125 

126 See additional notes in the ssh_test_server fixture above. 

127 """ 

128 # Note: The alt-server uses the same image as the ssh-server container, so 

129 # the id_rsa key and username should all match. 

130 # Only the host port it is allocate is different. 

131 alt_test_server_info = SshTestServerInfo( 

132 compose_project_name=ssh_test_server.compose_project_name, 

133 service_name=ALT_TEST_SERVER_NAME, 

134 hostname=ssh_test_server.hostname, 

135 username=ssh_test_server.username, 

136 id_rsa_path=ssh_test_server.id_rsa_path, 

137 ) 

138 wait_docker_service_socket( 

139 locked_docker_services, 

140 alt_test_server_info.hostname, 

141 alt_test_server_info.get_port(), 

142 ) 

143 return alt_test_server_info 

144 

145 

146@pytest.fixture(scope="session") 

147def reboot_test_server( 

148 ssh_test_server: SshTestServerInfo, 

149 locked_docker_services: DockerServices, 

150) -> SshTestServerInfo: 

151 """ 

152 Fixture for getting the third ssh test server info from the docker-compose.yml. 

153 

154 See additional notes in the ssh_test_server fixture above. 

155 """ 

156 # Note: The reboot-server uses the same image as the ssh-server container, so 

157 # the id_rsa key and username should all match. 

158 # Only the host port it is allocate is different. 

159 reboot_test_server_info = SshTestServerInfo( 

160 compose_project_name=ssh_test_server.compose_project_name, 

161 service_name=REBOOT_TEST_SERVER_NAME, 

162 hostname=ssh_test_server.hostname, 

163 username=ssh_test_server.username, 

164 id_rsa_path=ssh_test_server.id_rsa_path, 

165 ) 

166 wait_docker_service_socket( 

167 locked_docker_services, 

168 reboot_test_server_info.hostname, 

169 reboot_test_server_info.get_port(), 

170 ) 

171 return reboot_test_server_info 

172 

173 

174@pytest.fixture 

175def ssh_host_service(ssh_test_server: SshTestServerInfo) -> SshHostService: 

176 """Generic SshHostService fixture.""" 

177 return SshHostService( 

178 config={ 

179 "ssh_username": ssh_test_server.username, 

180 "ssh_priv_key_path": ssh_test_server.id_rsa_path, 

181 }, 

182 ) 

183 

184 

185@pytest.fixture 

186def ssh_fileshare_service() -> SshFileShareService: 

187 """Generic SshFileShareService fixture.""" 

188 return SshFileShareService( 

189 config={ 

190 # Left blank to make sure we test per connection overrides. 

191 }, 

192 )