github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/test/python/docker/compat/test_containers.py (about) 1 """ 2 Integration tests for exercising docker-py against Podman Service. 3 """ 4 import io 5 import tarfile 6 from typing import IO, List, Optional 7 8 from docker import errors 9 from docker.models.containers import Container 10 from docker.models.images import Image 11 from docker.models.volumes import Volume 12 from docker.types import Mount 13 14 # pylint: disable=no-name-in-module,import-error,wrong-import-order 15 from test.python.docker.compat import common, constant 16 17 18 # pylint: disable=missing-function-docstring 19 class TestContainers(common.DockerTestCase): 20 """TestCase for exercising containers.""" 21 22 def test_create_container(self): 23 """Run a container with detach mode.""" 24 self.docker.containers.create(image="alpine", detach=True) 25 self.assertEqual(len(self.docker.containers.list(all=True)), 2) 26 27 def test_create_network(self): 28 """Add network to a container.""" 29 self.docker.networks.create("testNetwork", driver="bridge") 30 self.docker.containers.create(image="alpine", detach=True) 31 32 def test_start_container(self): 33 # Podman docs says it should give a 304 but returns with no response 34 # # Start a already started container should return 304 35 # response = self.docker.api.start(container=self.top_container_id) 36 # self.assertEqual(error.exception.response.status_code, 304) 37 38 # Create a new container and validate the count 39 self.docker.containers.create(image=constant.ALPINE, name="container2") 40 containers = self.docker.containers.list(all=True) 41 self.assertEqual(len(containers), 2) 42 43 def test_start_container_with_random_port_bind(self): 44 container = self.docker.containers.create( 45 image=constant.ALPINE, 46 name="containerWithRandomBind", 47 ports={"1234/tcp": None}, 48 ) 49 containers = self.docker.containers.list(all=True) 50 self.assertTrue(container in containers) 51 52 def test_stop_container(self): 53 top = self.docker.containers.get(self.top_container_id) 54 self.assertEqual(top.status, "running") 55 56 # Stop a running container and validate the state 57 top.stop() 58 top.reload() 59 self.assertIn(top.status, ("stopped", "exited")) 60 61 def test_kill_container(self): 62 top = self.docker.containers.get(self.top_container_id) 63 self.assertEqual(top.status, "running") 64 65 # Kill a running container and validate the state 66 top.kill() 67 top.reload() 68 self.assertIn(top.status, ("stopped", "exited")) 69 70 def test_restart_container(self): 71 # Validate the container state 72 top = self.docker.containers.get(self.top_container_id) 73 top.stop() 74 top.reload() 75 self.assertIn(top.status, ("stopped", "exited")) 76 77 # restart a running container and validate the state 78 top.restart() 79 top.reload() 80 self.assertEqual(top.status, "running") 81 82 def test_remove_container(self): 83 # Remove container by ID with force 84 top = self.docker.containers.get(self.top_container_id) 85 top.remove(force=True) 86 self.assertEqual(len(self.docker.containers.list()), 0) 87 88 def test_remove_container_without_force(self): 89 # Validate current container count 90 self.assertEqual(len(self.docker.containers.list()), 1) 91 92 # Remove running container should throw error 93 top = self.docker.containers.get(self.top_container_id) 94 with self.assertRaises(errors.APIError) as error: 95 top.remove() 96 self.assertEqual(error.exception.response.status_code, 500) 97 98 # Remove container by ID without force 99 top.stop() 100 top.remove() 101 self.assertEqual(len(self.docker.containers.list()), 0) 102 103 def test_pause_container(self): 104 # Validate the container state 105 top = self.docker.containers.get(self.top_container_id) 106 self.assertEqual(top.status, "running") 107 108 # Pause a running container and validate the state 109 top.pause() 110 top.reload() 111 self.assertEqual(top.status, "paused") 112 113 def test_pause_stopped_container(self): 114 # Stop the container 115 top = self.docker.containers.get(self.top_container_id) 116 top.stop() 117 118 # Pause exited container should throw error 119 with self.assertRaises(errors.APIError) as error: 120 top.pause() 121 self.assertEqual(error.exception.response.status_code, 500) 122 123 def test_unpause_container(self): 124 top = self.docker.containers.get(self.top_container_id) 125 126 # Validate the container state 127 top.pause() 128 top.reload() 129 self.assertEqual(top.status, "paused") 130 131 # Pause a running container and validate the state 132 top.unpause() 133 top.reload() 134 self.assertEqual(top.status, "running") 135 136 def test_list_container(self): 137 # Add container and validate the count 138 self.docker.containers.create(image="alpine", detach=True) 139 containers = self.docker.containers.list(all=True) 140 self.assertEqual(len(containers), 2) 141 142 def test_filters(self): 143 self.skipTest("TODO Endpoint does not yet support filters") 144 145 # List container with filter by id 146 filters = {"id": self.top_container_id} 147 ctnrs = self.docker.containers.list(all=True, filters=filters) 148 self.assertEqual(len(ctnrs), 1) 149 150 # List container with filter by name 151 filters = {"name": "top"} 152 ctnrs = self.docker.containers.list(all=True, filters=filters) 153 self.assertEqual(len(ctnrs), 1) 154 155 def test_copy_to_container(self): 156 ctr: Optional[Container] = None 157 vol: Optional[Volume] = None 158 try: 159 test_file_content = b"Hello World!" 160 vol = self.docker.volumes.create("test-volume") 161 ctr = self.docker.containers.create( 162 image="alpine", 163 detach=True, 164 command="top", 165 volumes=["test-volume:/test-volume-read-only:ro"], 166 ) 167 ctr.start() 168 169 buff: IO[bytes] = io.BytesIO() 170 with tarfile.open(fileobj=buff, mode="w:") as file: 171 info: tarfile.TarInfo = tarfile.TarInfo() 172 info.uid = 1042 173 info.gid = 1043 174 info.name = "a.txt" 175 info.path = "a.txt" 176 info.mode = 0o644 177 info.type = tarfile.REGTYPE 178 info.size = len(test_file_content) 179 file.addfile(info, fileobj=io.BytesIO(test_file_content)) 180 181 buff.seek(0) 182 ctr.put_archive("/tmp/", buff) 183 ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/tmp/a.txt"]) 184 185 self.assertEqual(ret, 0) 186 self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID of copied file") 187 188 ret, out = ctr.exec_run(["cat", "/tmp/a.txt"]) 189 self.assertEqual(ret, 0) 190 self.assertEqual(out.rstrip(), test_file_content, "Content of copied file") 191 192 buff.seek(0) 193 with self.assertRaises(errors.APIError): 194 ctr.put_archive("/test-volume-read-only/", buff) 195 finally: 196 if ctr is not None: 197 ctr.stop() 198 ctr.remove() 199 if vol is not None: 200 vol.remove(force=True) 201 202 def test_mount_preexisting_dir(self): 203 dockerfile = ( 204 b"FROM quay.io/libpod/alpine:latest\n" 205 b"USER root\n" 206 b"RUN mkdir -p /workspace\n" 207 b"RUN chown 1042:1043 /workspace" 208 ) 209 img: Image 210 img, out = self.docker.images.build(fileobj=io.BytesIO(dockerfile)) 211 ctr: Container = self.docker.containers.create( 212 image=img.id, 213 detach=True, 214 command="top", 215 volumes=["test_mount_preexisting_dir_vol:/workspace"], 216 ) 217 ctr.start() 218 _, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"]) 219 self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID set in dockerfile") 220 221 def test_non_existant_workdir(self): 222 dockerfile = ( 223 b"FROM quay.io/libpod/alpine:latest\n" 224 b"USER root\n" 225 b"WORKDIR /workspace/scratch\n" 226 b"RUN touch test" 227 ) 228 img: Image 229 img, _ = self.docker.images.build(fileobj=io.BytesIO(dockerfile)) 230 ctr: Container = self.docker.containers.create( 231 image=img.id, 232 detach=True, 233 command="top", 234 volumes=["test_non_existant_workdir:/workspace"], 235 ) 236 ctr.start() 237 ret, _ = ctr.exec_run(["stat", "/workspace/scratch/test"]) 238 self.assertEqual(ret, 0, "Working directory created if it doesn't exist") 239 240 def test_mount_rw_by_default(self): 241 ctr: Optional[Container] = None 242 vol: Optional[Volume] = None 243 244 try: 245 vol = self.docker.volumes.create("test-volume") 246 ctr = self.docker.containers.create( 247 image="alpine", 248 detach=True, 249 command="top", 250 mounts=[ 251 Mount(target="/vol-mnt", source="test-volume", type="volume", read_only=False) 252 ], 253 ) 254 ctr_inspect = self.docker.api.inspect_container(ctr.id) 255 binds: List[str] = ctr_inspect["HostConfig"]["Binds"] 256 self.assertEqual(len(binds), 1) 257 self.assertEqual(binds[0], "test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind") 258 finally: 259 if ctr is not None: 260 ctr.remove() 261 if vol is not None: 262 vol.remove(force=True)