github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/test/apiv2/rest_api/test_rest_v2_0_0.py (about)

     1  import json
     2  import subprocess
     3  import sys
     4  import time
     5  import unittest
     6  from multiprocessing import Process
     7  
     8  import requests
     9  from dateutil.parser import parse
    10  
    11  from test.apiv2.rest_api import Podman
    12  
    13  PODMAN_URL = "http://localhost:8080"
    14  
    15  
    16  def _url(path):
    17      return PODMAN_URL + "/v2.0.0/libpod" + path
    18  
    19  
    20  def ctnr(path):
    21      try:
    22          r = requests.get(_url("/containers/json?all=true"))
    23          ctnrs = json.loads(r.text)
    24      except Exception as e:
    25          msg = f"Bad container response: {e}"
    26          if r is not None:
    27              msg = msg + " " + r.text
    28          sys.stderr.write(msg + "\n")
    29          raise
    30      return path.format(ctnrs[0]["Id"])
    31  
    32  
    33  def validateObjectFields(buffer):
    34      objs = json.loads(buffer)
    35      if not isinstance(objs, dict):
    36          for o in objs:
    37              _ = o["Id"]
    38      else:
    39          _ = objs["Id"]
    40      return objs
    41  
    42  
    43  class TestApi(unittest.TestCase):
    44      podman = None  # initialized podman configuration for tests
    45      service = None  # podman service instance
    46  
    47      def setUp(self):
    48          super().setUp()
    49  
    50          try:
    51              TestApi.podman.run("run", "alpine", "/bin/ls", check=True)
    52          except subprocess.CalledProcessError as e:
    53              if e.stdout:
    54                  sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8"))
    55              if e.stderr:
    56                  sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8"))
    57              raise
    58  
    59      @classmethod
    60      def setUpClass(cls):
    61          super().setUpClass()
    62  
    63          TestApi.podman = Podman()
    64          TestApi.service = TestApi.podman.open("system", "service", "tcp:localhost:8080", "--time=0")
    65          # give the service some time to be ready...
    66          time.sleep(2)
    67  
    68          returncode = TestApi.service.poll()
    69          if returncode is not None:
    70              raise subprocess.CalledProcessError(returncode, "podman system service")
    71  
    72          r = requests.post(_url("/images/pull?reference=docker.io%2Falpine%3Alatest"))
    73          if r.status_code != 200:
    74              raise subprocess.CalledProcessError(
    75                  r.status_code, f"podman images pull docker.io/alpine:latest {r.text}"
    76              )
    77  
    78      @classmethod
    79      def tearDownClass(cls):
    80          TestApi.service.terminate()
    81          stdout, stderr = TestApi.service.communicate(timeout=0.5)
    82          if stdout:
    83              sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8"))
    84          if stderr:
    85              sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8"))
    86          return super().tearDownClass()
    87  
    88      def test_info(self):
    89          r = requests.get(_url("/info"))
    90          self.assertEqual(r.status_code, 200)
    91          self.assertIsNotNone(r.content)
    92          _ = json.loads(r.text)
    93  
    94      def test_events(self):
    95          r = requests.get(_url("/events?stream=false"))
    96          self.assertEqual(r.status_code, 200, r.text)
    97          self.assertIsNotNone(r.content)
    98          for line in r.text.splitlines():
    99              obj = json.loads(line)
   100              # Actor.ID is uppercase for compatibility
   101              _ = obj["Actor"]["ID"]
   102  
   103      def test_containers(self):
   104          r = requests.get(_url("/containers/json"), timeout=5)
   105          self.assertEqual(r.status_code, 200, r.text)
   106          obj = json.loads(r.text)
   107          self.assertEqual(len(obj), 0)
   108  
   109      def test_containers_all(self):
   110          r = requests.get(_url("/containers/json?all=true"))
   111          self.assertEqual(r.status_code, 200, r.text)
   112          validateObjectFields(r.text)
   113  
   114      def test_inspect_container(self):
   115          r = requests.get(_url(ctnr("/containers/{}/json")))
   116          self.assertEqual(r.status_code, 200, r.text)
   117          obj = validateObjectFields(r.content)
   118          _ = parse(obj["Created"])
   119  
   120      def test_stats(self):
   121          r = requests.get(_url(ctnr("/containers/{}/stats?stream=false")))
   122          self.assertIn(r.status_code, (200, 409), r.text)
   123          if r.status_code == 200:
   124              validateObjectFields(r.text)
   125  
   126      def test_delete_containers(self):
   127          r = requests.delete(_url(ctnr("/containers/{}")))
   128          self.assertEqual(r.status_code, 204, r.text)
   129  
   130      def test_stop_containers(self):
   131          r = requests.post(_url(ctnr("/containers/{}/start")))
   132          self.assertIn(r.status_code, (204, 304), r.text)
   133  
   134          r = requests.post(_url(ctnr("/containers/{}/stop")))
   135          self.assertIn(r.status_code, (204, 304), r.text)
   136  
   137      def test_start_containers(self):
   138          r = requests.post(_url(ctnr("/containers/{}/stop")))
   139          self.assertIn(r.status_code, (204, 304), r.text)
   140  
   141          r = requests.post(_url(ctnr("/containers/{}/start")))
   142          self.assertIn(r.status_code, (204, 304), r.text)
   143  
   144      def test_restart_containers(self):
   145          r = requests.post(_url(ctnr("/containers/{}/start")))
   146          self.assertIn(r.status_code, (204, 304), r.text)
   147  
   148          r = requests.post(_url(ctnr("/containers/{}/restart")), timeout=5)
   149          self.assertEqual(r.status_code, 204, r.text)
   150  
   151      def test_resize(self):
   152          r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80")))
   153          self.assertIn(r.status_code, (200, 409), r.text)
   154          if r.status_code == 200:
   155              self.assertIsNone(r.text)
   156  
   157      def test_attach_containers(self):
   158          self.skipTest("FIXME: Test timeouts")
   159          r = requests.post(_url(ctnr("/containers/{}/attach")), timeout=5)
   160          self.assertIn(r.status_code, (101, 500), r.text)
   161  
   162      def test_logs_containers(self):
   163          r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true")))
   164          self.assertEqual(r.status_code, 200, r.text)
   165  
   166      # TODO Need to support Docker-py order of network/container creates
   167      def test_post_create_compat_connect(self):
   168          """Create network and container then connect to network"""
   169          net_default = requests.post(
   170              PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestDefaultNetwork"}
   171          )
   172          self.assertEqual(net_default.status_code, 201, net_default.text)
   173  
   174          create = requests.post(
   175              PODMAN_URL + "/v1.40/containers/create?name=postCreateConnect",
   176              json={
   177                  "Cmd": ["top"],
   178                  "Image": "alpine:latest",
   179                  "NetworkDisabled": False,
   180                  # FIXME adding these 2 lines cause: (This is sampled from docker-py)
   181                  #   "network already exists","message":"container
   182                  #  01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI network \"TestDefaultNetwork\": network already exists"
   183                  # "HostConfig": {"NetworkMode": "TestDefaultNetwork"},
   184                  # "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}},
   185                  # FIXME These two lines cause:
   186                  # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not found"
   187                  # "HostConfig": {"NetworkMode": "TestNetwork"},
   188                  # "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}},
   189                  # FIXME no networking defined cause: (note this error is from the container inspect below)
   190                  # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error"
   191              },
   192          )
   193          self.assertEqual(create.status_code, 201, create.text)
   194          payload = json.loads(create.text)
   195          self.assertIsNotNone(payload["Id"])
   196  
   197          start = requests.post(PODMAN_URL + f"/v1.40/containers/{payload['Id']}/start")
   198          self.assertEqual(start.status_code, 204, start.text)
   199  
   200          connect = requests.post(
   201              PODMAN_URL + "/v1.40/networks/TestDefaultNetwork/connect",
   202              json={"Container": payload["Id"]},
   203          )
   204          self.assertEqual(connect.status_code, 200, connect.text)
   205          self.assertEqual(connect.text, "OK\n")
   206  
   207          inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json")
   208          self.assertEqual(inspect.status_code, 200, inspect.text)
   209  
   210          payload = json.loads(inspect.text)
   211          self.assertFalse(payload["Config"].get("NetworkDisabled", False))
   212  
   213          self.assertEqual(
   214              "TestDefaultNetwork",
   215              payload["NetworkSettings"]["Networks"]["TestDefaultNetwork"]["NetworkID"],
   216          )
   217          # TODO restore this to test, when joining multiple networks possible
   218          # self.assertEqual(
   219          #     "TestNetwork",
   220          #     payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"],
   221          # )
   222          # TODO Need to support network aliases
   223          # self.assertIn(
   224          #     "test_post_create",
   225          #     payload["NetworkSettings"]["Networks"]["TestNetwork"]["Aliases"],
   226          # )
   227  
   228      def test_post_create_compat(self):
   229          """Create network and connect container during create"""
   230          net = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"})
   231          self.assertEqual(net.status_code, 201, net.text)
   232  
   233          create = requests.post(
   234              PODMAN_URL + "/v1.40/containers/create?name=postCreate",
   235              json={
   236                  "Cmd": ["date"],
   237                  "Image": "alpine:latest",
   238                  "NetworkDisabled": False,
   239                  "HostConfig": {"NetworkMode": "TestNetwork"},
   240              },
   241          )
   242          self.assertEqual(create.status_code, 201, create.text)
   243          payload = json.loads(create.text)
   244          self.assertIsNotNone(payload["Id"])
   245  
   246          inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json")
   247          self.assertEqual(inspect.status_code, 200, inspect.text)
   248          payload = json.loads(inspect.text)
   249          self.assertFalse(payload["Config"].get("NetworkDisabled", False))
   250          self.assertEqual(
   251              "TestNetwork",
   252              payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"],
   253          )
   254  
   255      def test_commit(self):
   256          r = requests.post(_url(ctnr("/commit?container={}")))
   257          self.assertEqual(r.status_code, 200, r.text)
   258          validateObjectFields(r.text)
   259  
   260      def test_images(self):
   261          r = requests.get(_url("/images/json"))
   262          self.assertEqual(r.status_code, 200, r.text)
   263          validateObjectFields(r.content)
   264  
   265      def test_inspect_image(self):
   266          r = requests.get(_url("/images/alpine/json"))
   267          self.assertEqual(r.status_code, 200, r.text)
   268          obj = validateObjectFields(r.content)
   269          _ = parse(obj["Created"])
   270  
   271      def test_delete_image(self):
   272          r = requests.delete(_url("/images/alpine?force=true"))
   273          self.assertEqual(r.status_code, 200, r.text)
   274          json.loads(r.text)
   275  
   276      def test_pull(self):
   277          r = requests.post(_url("/images/pull?reference=alpine"), timeout=15)
   278          self.assertEqual(r.status_code, 200, r.status_code)
   279          text = r.text
   280          keys = {
   281              "error": False,
   282              "id": False,
   283              "images": False,
   284              "stream": False,
   285          }
   286          # Read and record stanza's from pull
   287          for line in str.splitlines(text):
   288              obj = json.loads(line)
   289              key_list = list(obj.keys())
   290              for k in key_list:
   291                  keys[k] = True
   292  
   293          self.assertFalse(keys["error"], "Expected no errors")
   294          self.assertTrue(keys["id"], "Expected to find id stanza")
   295          self.assertTrue(keys["images"], "Expected to find images stanza")
   296          self.assertTrue(keys["stream"], "Expected to find stream progress stanza's")
   297  
   298      def test_search(self):
   299          # Had issues with this test hanging when repositories not happy
   300          def do_search():
   301              r = requests.get(_url("/images/search?term=alpine"), timeout=5)
   302              self.assertEqual(r.status_code, 200, r.text)
   303              json.loads(r.text)
   304  
   305          search = Process(target=do_search)
   306          search.start()
   307          search.join(timeout=10)
   308          self.assertFalse(search.is_alive(), "/images/search took too long")
   309  
   310      def test_ping(self):
   311          r = requests.get(PODMAN_URL + "/_ping")
   312          self.assertEqual(r.status_code, 200, r.text)
   313  
   314          r = requests.head(PODMAN_URL + "/_ping")
   315          self.assertEqual(r.status_code, 200, r.text)
   316  
   317          r = requests.get(_url("/_ping"))
   318          self.assertEqual(r.status_code, 200, r.text)
   319  
   320          r = requests.get(_url("/_ping"))
   321          self.assertEqual(r.status_code, 200, r.text)
   322  
   323  
   324  if __name__ == "__main__":
   325      unittest.main()