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()