summaryrefslogtreecommitdiff
path: root/test/apiv2
diff options
context:
space:
mode:
Diffstat (limited to 'test/apiv2')
-rw-r--r--test/apiv2/10-images.at6
-rw-r--r--test/apiv2/40-pods.at4
-rw-r--r--test/apiv2/rest_api/__init__.py0
-rw-r--r--test/apiv2/rest_api/test_rest_v1_0_0.py219
4 files changed, 224 insertions, 5 deletions
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index 2a1eaf8be..1c7ba8948 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -7,15 +7,15 @@
podman pull -q $IMAGE
t GET libpod/images/json 200 \
- .[0].ID~[0-9a-f]\\{64\\}
-iid=$(jq -r '.[0].ID' <<<"$output")
+ .[0].Id~[0-9a-f]\\{64\\}
+iid=$(jq -r '.[0].Id' <<<"$output")
t GET libpod/images/$iid/exists 204
t GET libpod/images/$PODMAN_TEST_IMAGE_NAME/exists 204
# FIXME: compare to actual podman info
t GET libpod/images/json 200 \
- .[0].ID=${iid}
+ .[0].Id=${iid}
t GET libpod/images/$iid/json 200 \
.Id=$iid \
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index 26877a102..2dea1918a 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -5,8 +5,8 @@
t GET "libpod/pods/json (clean slate at start)" 200 null
-t POST libpod/pods/create name=foo 201 .id~[0-9a-f]\\{64\\}
-pod_id=$(jq -r .id <<<"$output")
+t POST libpod/pods/create name=foo 201 .Id~[0-9a-f]\\{64\\}
+pod_id=$(jq -r .Id <<<"$output")
t GET libpod/pods/foo/exists 204
t GET libpod/pods/$pod_id/exists 204
t GET libpod/pods/notfoo/exists 404
diff --git a/test/apiv2/rest_api/__init__.py b/test/apiv2/rest_api/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/apiv2/rest_api/__init__.py
diff --git a/test/apiv2/rest_api/test_rest_v1_0_0.py b/test/apiv2/rest_api/test_rest_v1_0_0.py
new file mode 100644
index 000000000..7c53623cb
--- /dev/null
+++ b/test/apiv2/rest_api/test_rest_v1_0_0.py
@@ -0,0 +1,219 @@
+import json
+import os
+import shlex
+import signal
+import string
+import subprocess
+import sys
+import time
+import unittest
+from collections.abc import Iterable
+from multiprocessing import Process
+
+import requests
+from dateutil.parser import parse
+
+
+def _url(path):
+ return "http://localhost:8080/v1.0.0/libpod" + path
+
+
+def podman():
+ binary = os.getenv("PODMAN_BINARY")
+ if binary is None:
+ binary = "bin/podman"
+ return binary
+
+
+def ctnr(path):
+ r = requests.get(_url("/containers/json?all=true"))
+ try:
+ ctnrs = json.loads(r.text)
+ except Exception as e:
+ sys.stderr.write("Bad container response: {}/{}".format(r.text, e))
+ raise e
+ return path.format(ctnrs[0]["Id"])
+
+
+class TestApi(unittest.TestCase):
+ podman = None
+
+ def setUp(self):
+ super().setUp()
+ if TestApi.podman.poll() is not None:
+ sys.stderr.write("podman service returned {}",
+ TestApi.podman.returncode)
+ sys.exit(2)
+ requests.get(
+ _url("/images/create?fromSrc=docker.io%2Falpine%3Alatest"))
+ # calling out to podman is easier than the API for running a container
+ subprocess.run([podman(), "run", "alpine", "/bin/ls"],
+ check=True,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL)
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ TestApi.podman = subprocess.Popen(
+ [
+ podman(), "system", "service", "tcp:localhost:8080",
+ "--log-level=debug", "--time=0"
+ ],
+ shell=False,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ time.sleep(2)
+
+ @classmethod
+ def tearDownClass(cls):
+ TestApi.podman.terminate()
+ stdout, stderr = TestApi.podman.communicate(timeout=0.5)
+ if stdout:
+ print("\nService Stdout:\n" + stdout.decode('utf-8'))
+ if stderr:
+ print("\nService Stderr:\n" + stderr.decode('utf-8'))
+
+ if TestApi.podman.returncode > 0:
+ sys.stderr.write("podman exited with error code {}\n".format(
+ TestApi.podman.returncode))
+ sys.exit(2)
+
+ return super().tearDownClass()
+
+ def test_info(self):
+ r = requests.get(_url("/info"))
+ self.assertEqual(r.status_code, 200)
+ self.assertIsNotNone(r.content)
+ _ = json.loads(r.text)
+
+ def test_events(self):
+ r = requests.get(_url("/events?stream=false"))
+ self.assertEqual(r.status_code, 200, r.text)
+ self.assertIsNotNone(r.content)
+ for line in r.text.splitlines():
+ obj = json.loads(line)
+ # Actor.ID is uppercase for compatibility
+ _ = obj["Actor"]["ID"]
+
+ def test_containers(self):
+ r = requests.get(_url("/containers/json"), timeout=5)
+ self.assertEqual(r.status_code, 200, r.text)
+ obj = json.loads(r.text)
+ self.assertEqual(len(obj), 0)
+
+ def test_containers_all(self):
+ r = requests.get(_url("/containers/json?all=true"))
+ self.assertEqual(r.status_code, 200, r.text)
+ self.validateObjectFields(r.text)
+
+ def test_inspect_container(self):
+ r = requests.get(_url(ctnr("/containers/{}/json")))
+ self.assertEqual(r.status_code, 200, r.text)
+ obj = self.validateObjectFields(r.content)
+ _ = parse(obj["Created"])
+
+ def test_stats(self):
+ r = requests.get(_url(ctnr("/containers/{}/stats?stream=false")))
+ self.assertIn(r.status_code, (200, 409), r.text)
+ if r.status_code == 200:
+ self.validateObjectFields(r.text)
+
+ def test_delete_containers(self):
+ r = requests.delete(_url(ctnr("/containers/{}")))
+ self.assertEqual(r.status_code, 204, r.text)
+
+ def test_stop_containers(self):
+ r = requests.post(_url(ctnr("/containers/{}/start")))
+ self.assertIn(r.status_code, (204, 304), r.text)
+
+ r = requests.post(_url(ctnr("/containers/{}/stop")))
+ self.assertIn(r.status_code, (204, 304), r.text)
+
+ def test_start_containers(self):
+ r = requests.post(_url(ctnr("/containers/{}/stop")))
+ self.assertIn(r.status_code, (204, 304), r.text)
+
+ r = requests.post(_url(ctnr("/containers/{}/start")))
+ self.assertIn(r.status_code, (204, 304), r.text)
+
+ def test_restart_containers(self):
+ r = requests.post(_url(ctnr("/containers/{}/start")))
+ self.assertIn(r.status_code, (204, 304), r.text)
+
+ r = requests.post(_url(ctnr("/containers/{}/restart")), timeout=5)
+ self.assertEqual(r.status_code, 204, r.text)
+
+ def test_resize(self):
+ r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80")))
+ self.assertIn(r.status_code, (200, 409), r.text)
+ if r.status_code == 200:
+ self.assertIsNone(r.text)
+
+ def test_attach_containers(self):
+ r = requests.post(_url(ctnr("/containers/{}/attach")))
+ self.assertIn(r.status_code, (101, 409), r.text)
+
+ def test_logs_containers(self):
+ r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true")))
+ self.assertEqual(r.status_code, 200, r.text)
+
+ def test_post_create(self):
+ self.skipTest("TODO: create request body")
+ r = requests.post(_url("/containers/create?args=True"))
+ self.assertEqual(r.status_code, 200, r.text)
+ json.loads(r.text)
+
+ def test_commit(self):
+ r = requests.post(_url(ctnr("/commit?container={}")))
+ self.assertEqual(r.status_code, 200, r.text)
+ self.validateObjectFields(r.text)
+
+ def test_images(self):
+ r = requests.get(_url("/images/json"))
+ self.assertEqual(r.status_code, 200, r.text)
+ self.validateObjectFields(r.content)
+
+ def test_inspect_image(self):
+ r = requests.get(_url("/images/alpine/json"))
+ self.assertEqual(r.status_code, 200, r.text)
+ obj = self.validateObjectFields(r.content)
+ _ = parse(obj["Created"])
+
+ def test_delete_image(self):
+ r = requests.delete(_url("/images/alpine?force=true"))
+ self.assertEqual(r.status_code, 200, r.text)
+ json.loads(r.text)
+
+ def test_pull(self):
+ r = requests.post(_url("/images/pull?reference=alpine"), timeout=5)
+ self.assertEqual(r.status_code, 200, r.text)
+ json.loads(r.text)
+
+ def test_search(self):
+ # Had issues with this test hanging when repositories not happy
+ def do_search():
+ r = requests.get(_url("/images/search?term=alpine"), timeout=5)
+ self.assertEqual(r.status_code, 200, r.text)
+ json.loads(r.text)
+
+ search = Process(target=do_search)
+ search.start()
+ search.join(timeout=10)
+ self.assertFalse(search.is_alive(), "/images/search took too long")
+
+ def validateObjectFields(self, buffer):
+ objs = json.loads(buffer)
+ if not isinstance(objs, dict):
+ for o in objs:
+ _ = o["Id"]
+ else:
+ _ = objs["Id"]
+ return objs
+
+
+if __name__ == '__main__':
+ unittest.main()