test_podman.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import infra.basetest
  2. import json
  3. import os
  4. class PodmanBase(infra.basetest.BRTest):
  5. config = \
  6. """
  7. BR2_arm=y
  8. BR2_cortex_a9=y
  9. BR2_ARM_ENABLE_VFP=y
  10. BR2_TOOLCHAIN_EXTERNAL=y
  11. BR2_TOOLCHAIN_EXTERNAL_BOOTLIN=y
  12. BR2_PER_PACKAGE_DIRECTORIES=y
  13. BR2_SYSTEM_DHCP="eth0"
  14. BR2_LINUX_KERNEL=y
  15. BR2_LINUX_KERNEL_CUSTOM_VERSION=y
  16. BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="5.10.202"
  17. BR2_LINUX_KERNEL_DEFCONFIG="vexpress"
  18. BR2_LINUX_KERNEL_DTS_SUPPORT=y
  19. BR2_LINUX_KERNEL_INTREE_DTS_NAME="vexpress-v2p-ca9"
  20. BR2_PACKAGE_PODMAN=y
  21. BR2_PACKAGE_UTIL_LINUX=y
  22. BR2_PACKAGE_UTIL_LINUX_MOUNT=y
  23. BR2_PACKAGE_HOST_GO_BIN=y
  24. BR2_TARGET_ROOTFS_EXT2=y
  25. BR2_TARGET_ROOTFS_EXT2_SIZE="256M"
  26. # BR2_TARGET_ROOTFS_TAR is not set
  27. """
  28. def do_test(self):
  29. class _Emul():
  30. def __init__(self, orig_emulator):
  31. self.emulator = orig_emulator
  32. def run(self, cmd, timeout=-1):
  33. if timeout < 0:
  34. timeout = 60
  35. return self.emulator.run(cmd, timeout)
  36. def stop(self):
  37. self.emulator.stop()
  38. kernel_file = os.path.join(self.builddir, 'images', 'zImage')
  39. dtb_file = os.path.join(self.builddir, 'images', 'vexpress-v2p-ca9.dtb')
  40. ext2_file = os.path.join(self.builddir, 'images', 'rootfs.ext2')
  41. self.emulator.boot(
  42. arch='armv5',
  43. kernel=kernel_file,
  44. kernel_cmdline=[
  45. 'root=/dev/mmcblk0',
  46. 'rootwait',
  47. 'console=ttyAMA0',
  48. ],
  49. options=[
  50. '-M', 'vexpress-a9',
  51. '-dtb', dtb_file,
  52. '-drive', f'file={ext2_file},if=sd,format=raw',
  53. ]
  54. )
  55. self.emulator.login()
  56. # Trick: replace the original emulator with one that always
  57. # adds a timeout
  58. self.emulator = _Emul(self.emulator)
  59. # Do some preparation for rootless use
  60. self.assertRunOk("mount --make-shared /")
  61. self.assertRunOk("chmod 666 /dev/net/tun")
  62. self.assertRunOk("useradd -d /home/foo -m -s /bin/sh -u 1000 foo")
  63. self.assertRunOk("touch /etc/subuid /etc/subgid")
  64. self.assertRunOk("usermod --add-subuids 10000-75535 foo")
  65. self.assertRunOk("usermod --add-subgids 10000-75535 foo")
  66. # If /etc/resolv.conf is a symlink, it has to point either into /etc
  67. # (or deep in there), or into /run (or deep in there), as only those
  68. # would eventually get used by podman/netavark for # rootless containers.
  69. # This is considered a workaround to the current situation; resolv,conf
  70. # should ultimately be in /run rather than /tmp.
  71. self.assertRunOk("mv /tmp/resolv.conf /run/resolv.conf")
  72. self.assertRunOk("ln -sf /run/resolv.conf /etc/resolv.conf")
  73. # First, test podman as root (the current user)
  74. self.do_podman()
  75. # Now, test podman as non-root. We need a bit of setup
  76. # We need to use the same prompts for the user as used for root, so that the
  77. # REPLWrapper still detects the prompts. This means it is going to be a bit
  78. # difficut to directly see that it was a user that executed a command.
  79. self.assertRunOk('su -s /usr/bin/env - foo PS1="${PS1}" PS2="${PS2}" /bin/sh')
  80. output, _ = self.emulator.run("id -u")
  81. self.assertEqual(output[0], "1000", "Could not switch to non-root")
  82. self.do_podman()
  83. def do_podman(self):
  84. # The podman binary is huge, so it takes time to load...
  85. # Next calls will be faster, though, as it is going to be cached.
  86. self.assertRunOk('podman --version')
  87. # Check for an empty image store
  88. output, exit_code = self.emulator.run("podman image ls --format '{{ json }}'")
  89. img_info = json.loads("".join(output))
  90. self.assertEqual(len(img_info), 0, f"{len(img_info)} image(s) already present")
  91. # Pull an image; it can take time: network, hash checksums...
  92. self.assertRunOk('podman image pull busybox:1.37.0')
  93. output, exit_code = self.emulator.run("podman image ls --format '{{ json }}'")
  94. img_info = json.loads("".join(output))
  95. self.assertEqual(len(img_info), 1, f"{len(img_info)} image(s), expecting 1")
  96. self.assertTrue("Id" in img_info[0], '"Id" not in img_info[0]')
  97. self.assertTrue("Digest" in img_info[0], '"Digest" not in img_info[0]')
  98. self.assertEqual(img_info[0]["Names"][0], "docker.io/library/busybox:1.37.0")
  99. output, _ = self.emulator.run('echo ${br_container}')
  100. self.assertEqual(output[0], "", "Already in a container")
  101. # Spawn the container; that can take a bit of time
  102. # Propagate the prompt so that the REPLWrapper detects it
  103. self.assertRunOk(
  104. "podman container run --rm -ti -e PS1 -e br_container=podman busybox:1.37.0",
  105. )
  106. # Twist! The command above is still running, but the shell it
  107. # started exposes the same prompt we expect. This is all what we want.
  108. output, _ = self.emulator.run('echo ${br_container}')
  109. self.assertEqual(output[0], "podman", "Not in a podman container")
  110. # Check that pid1 is the shell
  111. output, _ = self.emulator.run('readlink /proc/1/exe')
  112. self.assertEqual(output[0], "/bin/sh", f"PID1 is {output[0]}, should be /bin/sh")
  113. # Try to get something off the network
  114. # Using http, not https, as busybox' wget does not do https
  115. # Using --spider to just check we can reach the remote.
  116. output, exit_code = self.emulator.run('wget --spider http://google.com/')
  117. self.assertEqual(exit_code, 0, "wget did not succeed to reach google.com")
  118. self.assertEqual(output[-1], "remote file exists", "wget did not succeed to reach google.com")
  119. # Exit the container
  120. self.assertRunOk("exit 0")
  121. # Twist, take two! We are now back to the shell in the VM.
  122. output, _ = self.emulator.run('echo ${br_container}')
  123. self.assertEqual(output[0], "", "Still in a container")
  124. # Spawn a container, round two, but with an injected init this time
  125. self.assertRunOk(
  126. "podman container run --rm -ti -e PS1 --init -e br_container=podman busybox:1.37.0",
  127. )
  128. output, _ = self.emulator.run('echo ${br_container}')
  129. self.assertEqual(output[0], "podman", "Not in a podman container")
  130. # Check that pid1 is the init injected by podman
  131. output, _ = self.emulator.run('readlink /proc/1/exe')
  132. self.assertEqual(output[0], "/run/podman-init", f"PID1 is {output[0]}, should be /run/podman-init")
  133. # Exit the container
  134. self.assertRunOk("exit 0")
  135. output, _ = self.emulator.run('echo ${br_container}')
  136. self.assertEqual(output[0], "", "Still in a container")
  137. # Use an image from another registry, spawn without pulling first
  138. self.assertRunOk(
  139. "podman container run --rm -ti -e PS1 -e br_container=podman quay.io/prometheus/busybox:latest",
  140. )
  141. output, _ = self.emulator.run('echo ${br_container}')
  142. self.assertEqual(output[0], "podman", "Not in a podman container")
  143. self.assertRunOk("exit 0")
  144. output, _ = self.emulator.run('echo ${br_container}')
  145. self.assertEqual(output[0], "", "Still in a container")
  146. # Remove the offical image
  147. self.assertRunOk('podman image rm busybox:1.37.0')
  148. output, _ = self.emulator.run("podman image ls --format '{{ json }}'")
  149. img_info = json.loads("".join(output))
  150. # There is still one image(the unofficial one from quay.io)
  151. self.assertEqual(len(img_info), 1, f"{len(img_info)} image(s) still present, expecting 1")
  152. # Remove all remaining images
  153. self.assertRunOk('podman image prune -af')
  154. output, exit_code = self.emulator.run("podman image ls --format '{{ json }}'")
  155. img_info = json.loads("".join(output))
  156. self.assertEqual(len(img_info), 0, f"{len(img_info)} image(s) still present, expecting 0")
  157. class TestPodmanIptables(PodmanBase):
  158. def test_run(self):
  159. self.do_test()
  160. class TestPodmanNftables(PodmanBase):
  161. config = PodmanBase.config + """
  162. BR2_PACKAGE_NFTABLES=y
  163. """
  164. def test_run(self):
  165. self.do_test()
  166. class TestPodmanTini(PodmanBase):
  167. config = PodmanBase.config + """
  168. BR2_PACKAGE_PODMAN_INIT_TINI=y
  169. """
  170. def test_run(self):
  171. self.do_test()