github.com/AbhinandanKurakure/podman/v3@v3.4.10/test/python/docker/compat/test_containers.py (about)

     1  import io
     2  import subprocess
     3  import sys
     4  import time
     5  import unittest
     6  from typing import IO, Optional, List
     7  
     8  from docker import DockerClient, 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  from test.python.docker import Podman
    15  from test.python.docker.compat import common, constant
    16  
    17  import tarfile
    18  
    19  
    20  class TestContainers(unittest.TestCase):
    21      podman = None  # initialized podman configuration for tests
    22      service = None  # podman service instance
    23      topContainerId = ""
    24  
    25      def setUp(self):
    26          super().setUp()
    27          self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15)
    28          TestContainers.podman.restore_image_from_cache(self.client)
    29          TestContainers.topContainerId = common.run_top_container(self.client)
    30          self.assertIsNotNone(TestContainers.topContainerId)
    31  
    32      def tearDown(self):
    33          common.remove_all_containers(self.client)
    34          common.remove_all_images(self.client)
    35          self.client.close()
    36          return super().tearDown()
    37  
    38      @classmethod
    39      def setUpClass(cls):
    40          super().setUpClass()
    41          TestContainers.podman = Podman()
    42          TestContainers.service = TestContainers.podman.open(
    43              "system", "service", "tcp:127.0.0.1:8080", "--time=0"
    44          )
    45          # give the service some time to be ready...
    46          time.sleep(2)
    47  
    48          rc = TestContainers.service.poll()
    49          if rc is not None:
    50              raise subprocess.CalledProcessError(rc, "podman system service")
    51  
    52      @classmethod
    53      def tearDownClass(cls):
    54          TestContainers.service.terminate()
    55          stdout, stderr = TestContainers.service.communicate(timeout=0.5)
    56          if stdout:
    57              sys.stdout.write("\nContainers Service Stdout:\n" + stdout.decode("utf-8"))
    58          if stderr:
    59              sys.stderr.write("\nContainers Service Stderr:\n" + stderr.decode("utf-8"))
    60  
    61          TestContainers.podman.tear_down()
    62          return super().tearDownClass()
    63  
    64      def test_create_container(self):
    65          # Run a container with detach mode
    66          self.client.containers.create(image="alpine", detach=True)
    67          self.assertEqual(len(self.client.containers.list(all=True)), 2)
    68  
    69      def test_create_network(self):
    70          net = self.client.networks.create("testNetwork", driver="bridge")
    71          ctnr = self.client.containers.create(image="alpine", detach=True)
    72  
    73          #  TODO fix when ready
    74          # This test will not work until all connect|disconnect
    75          # code is fixed.
    76          # net.connect(ctnr)
    77  
    78          # nets = self.client.networks.list(greedy=True)
    79          # self.assertGreaterEqual(len(nets), 1)
    80  
    81          # TODO fix endpoint to include containers
    82          # for n in nets:
    83          #     if n.id == "testNetwork":
    84          #         self.assertEqual(ctnr.id, n.containers)
    85          # self.assertTrue(False, "testNetwork not found")
    86  
    87      def test_start_container(self):
    88          # Podman docs says it should give a 304 but returns with no response
    89          # # Start a already started container should return 304
    90          # response = self.client.api.start(container=TestContainers.topContainerId)
    91          # self.assertEqual(error.exception.response.status_code, 304)
    92  
    93          # Create a new container and validate the count
    94          self.client.containers.create(image=constant.ALPINE, name="container2")
    95          containers = self.client.containers.list(all=True)
    96          self.assertEqual(len(containers), 2)
    97  
    98      def test_start_container_with_random_port_bind(self):
    99          container = self.client.containers.create(
   100              image=constant.ALPINE,
   101              name="containerWithRandomBind",
   102              ports={"1234/tcp": None},
   103          )
   104          containers = self.client.containers.list(all=True)
   105          self.assertTrue(container in containers)
   106  
   107      def test_stop_container(self):
   108          top = self.client.containers.get(TestContainers.topContainerId)
   109          self.assertEqual(top.status, "running")
   110  
   111          # Stop a running container and validate the state
   112          top.stop()
   113          top.reload()
   114          self.assertIn(top.status, ("stopped", "exited"))
   115  
   116      def test_kill_container(self):
   117          top = self.client.containers.get(TestContainers.topContainerId)
   118          self.assertEqual(top.status, "running")
   119  
   120          # Kill a running container and validate the state
   121          top.kill()
   122          top.reload()
   123          self.assertIn(top.status, ("stopped", "exited"))
   124  
   125      def test_restart_container(self):
   126          # Validate the container state
   127          top = self.client.containers.get(TestContainers.topContainerId)
   128          top.stop()
   129          top.reload()
   130          self.assertIn(top.status, ("stopped", "exited"))
   131  
   132          # restart a running container and validate the state
   133          top.restart()
   134          top.reload()
   135          self.assertEqual(top.status, "running")
   136  
   137      def test_remove_container(self):
   138          # Remove container by ID with force
   139          top = self.client.containers.get(TestContainers.topContainerId)
   140          top.remove(force=True)
   141          self.assertEqual(len(self.client.containers.list()), 0)
   142  
   143      def test_remove_container_without_force(self):
   144          # Validate current container count
   145          self.assertEqual(len(self.client.containers.list()), 1)
   146  
   147          # Remove running container should throw error
   148          top = self.client.containers.get(TestContainers.topContainerId)
   149          with self.assertRaises(errors.APIError) as error:
   150              top.remove()
   151          self.assertEqual(error.exception.response.status_code, 500)
   152  
   153          # Remove container by ID without force
   154          top.stop()
   155          top.remove()
   156          self.assertEqual(len(self.client.containers.list()), 0)
   157  
   158      def test_pause_container(self):
   159          # Validate the container state
   160          top = self.client.containers.get(TestContainers.topContainerId)
   161          self.assertEqual(top.status, "running")
   162  
   163          # Pause a running container and validate the state
   164          top.pause()
   165          top.reload()
   166          self.assertEqual(top.status, "paused")
   167  
   168      def test_pause_stopped_container(self):
   169          # Stop the container
   170          top = self.client.containers.get(TestContainers.topContainerId)
   171          top.stop()
   172  
   173          # Pause exited container should throw error
   174          with self.assertRaises(errors.APIError) as error:
   175              top.pause()
   176          self.assertEqual(error.exception.response.status_code, 500)
   177  
   178      def test_unpause_container(self):
   179          top = self.client.containers.get(TestContainers.topContainerId)
   180  
   181          # Validate the container state
   182          top.pause()
   183          top.reload()
   184          self.assertEqual(top.status, "paused")
   185  
   186          # Pause a running container and validate the state
   187          top.unpause()
   188          top.reload()
   189          self.assertEqual(top.status, "running")
   190  
   191      def test_list_container(self):
   192          # Add container and validate the count
   193          self.client.containers.create(image="alpine", detach=True)
   194          containers = self.client.containers.list(all=True)
   195          self.assertEqual(len(containers), 2)
   196  
   197      def test_filters(self):
   198          self.skipTest("TODO Endpoint does not yet support filters")
   199  
   200          # List container with filter by id
   201          filters = {"id": TestContainers.topContainerId}
   202          ctnrs = self.client.containers.list(all=True, filters=filters)
   203          self.assertEqual(len(ctnrs), 1)
   204  
   205          # List container with filter by name
   206          filters = {"name": "top"}
   207          ctnrs = self.client.containers.list(all=True, filters=filters)
   208          self.assertEqual(len(ctnrs), 1)
   209  
   210      def test_copy_to_container(self):
   211          ctr: Optional[Container] = None
   212          vol: Optional[Volume] = None
   213          try:
   214              test_file_content = b"Hello World!"
   215              vol = self.client.volumes.create("test-volume")
   216              ctr = self.client.containers.create(image="alpine",
   217                                                  detach=True,
   218                                                  command="top",
   219                                                  volumes=["test-volume:/test-volume-read-only:ro"])
   220              ctr.start()
   221  
   222              buff: IO[bytes] = io.BytesIO()
   223              with tarfile.open(fileobj=buff, mode="w:") as tf:
   224                  ti: tarfile.TarInfo = tarfile.TarInfo()
   225                  ti.uid = 1042
   226                  ti.gid = 1043
   227                  ti.name = "a.txt"
   228                  ti.path = "a.txt"
   229                  ti.mode = 0o644
   230                  ti.type = tarfile.REGTYPE
   231                  ti.size = len(test_file_content)
   232                  tf.addfile(ti, fileobj=io.BytesIO(test_file_content))
   233  
   234              buff.seek(0)
   235              ctr.put_archive("/tmp/", buff)
   236              ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/tmp/a.txt"])
   237  
   238              self.assertEqual(ret, 0)
   239              self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID of copied file")
   240  
   241              ret, out = ctr.exec_run(["cat", "/tmp/a.txt"])
   242              self.assertEqual(ret, 0)
   243              self.assertEqual(out.rstrip(), test_file_content, "Content of copied file")
   244  
   245              buff.seek(0)
   246              with self.assertRaises(errors.APIError):
   247                  ctr.put_archive("/test-volume-read-only/", buff)
   248          finally:
   249              if ctr is not None:
   250                  ctr.stop()
   251                  ctr.remove()
   252              if vol is not None:
   253                  vol.remove(force=True)
   254  
   255      def test_mount_preexisting_dir(self):
   256          dockerfile = (B'FROM quay.io/libpod/alpine:latest\n'
   257                        B'USER root\n'
   258                        B'RUN mkdir -p /workspace\n'
   259                        B'RUN chown 1042:1043 /workspace')
   260          img: Image
   261          img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile))
   262          ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top",
   263                                                         volumes=["test_mount_preexisting_dir_vol:/workspace"])
   264          ctr.start()
   265          ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"])
   266          self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID set in dockerfile")
   267  
   268  
   269      def test_non_existant_workdir(self):
   270          dockerfile = (B'FROM quay.io/libpod/alpine:latest\n'
   271                        B'USER root\n'
   272                        B'WORKDIR /workspace/scratch\n'
   273                        B'RUN touch test')
   274          img: Image
   275          img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile))
   276          ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top",
   277                                                         volumes=["test_non_existant_workdir:/workspace"])
   278          ctr.start()
   279          ret, out = ctr.exec_run(["stat", "/workspace/scratch/test"])
   280          self.assertEqual(ret, 0, "Working directory created if it doesn't exist")
   281  
   282      def test_mount_rw_by_default(self):
   283          ctr: Optional[Container] = None
   284          vol: Optional[Volume] = None
   285          try:
   286              vol = self.client.volumes.create("test-volume")
   287              ctr = self.client.containers.create(image="alpine",
   288                                                  detach=True,
   289                                                  command="top",
   290                                                  mounts=[Mount(target="/vol-mnt",
   291                                                                source="test-volume",
   292                                                                type='volume',
   293                                                                read_only=False)])
   294              ctr_inspect = self.client.api.inspect_container(ctr.id)
   295              binds: List[str] = ctr_inspect["HostConfig"]["Binds"]
   296              self.assertEqual(len(binds), 1)
   297              self.assertEqual(binds[0], 'test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind')
   298          finally:
   299              if ctr is not None:
   300                  ctr.remove()
   301              if vol is not None:
   302                  vol.remove(force=True)