forked from containers/podman
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request containers#10372 from jwhonce/issues/9238
Break up python APIv2 tests
- Loading branch information
Showing
15 changed files
with
865 additions
and
749 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .api_testcase import APITestCase | ||
|
||
__all__ = ["APITestCase"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import json | ||
import subprocess | ||
import unittest | ||
|
||
import requests | ||
import sys | ||
import time | ||
|
||
from .podman import Podman | ||
|
||
|
||
class APITestCase(unittest.TestCase): | ||
PODMAN_URL = "http://localhost:8080" | ||
podman = None # initialized podman configuration for tests | ||
service = None # podman service instance | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
|
||
APITestCase.podman = Podman() | ||
APITestCase.service = APITestCase.podman.open( | ||
"system", "service", "tcp:localhost:8080", "--time=0" | ||
) | ||
# give the service some time to be ready... | ||
time.sleep(2) | ||
|
||
returncode = APITestCase.service.poll() | ||
if returncode is not None: | ||
raise subprocess.CalledProcessError(returncode, "podman system service") | ||
|
||
r = requests.post( | ||
APITestCase.uri("/images/pull?reference=quay.io%2Flibpod%2Falpine%3Alatest") | ||
) | ||
if r.status_code != 200: | ||
raise subprocess.CalledProcessError( | ||
r.status_code, f"podman images pull quay.io/libpod/alpine:latest {r.text}" | ||
) | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
APITestCase.service.terminate() | ||
stdout, stderr = APITestCase.service.communicate(timeout=0.5) | ||
if stdout: | ||
sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8")) | ||
if stderr: | ||
sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8")) | ||
return super().tearDownClass() | ||
|
||
def setUp(self): | ||
super().setUp() | ||
APITestCase.podman.run("run", "alpine", "/bin/ls", check=True) | ||
|
||
def tearDown(self) -> None: | ||
APITestCase.podman.run("pod", "rm", "--all", "--force", check=True) | ||
APITestCase.podman.run("rm", "--all", "--force", check=True) | ||
super().tearDown() | ||
|
||
@property | ||
def podman_url(self): | ||
return "http://localhost:8080" | ||
|
||
@staticmethod | ||
def uri(path): | ||
return APITestCase.PODMAN_URL + "/v2.0.0/libpod" + path | ||
|
||
def resolve_container(self, path): | ||
"""Find 'first' container and return 'Id' formatted into given URI path.""" | ||
|
||
try: | ||
r = requests.get(self.uri("/containers/json?all=true")) | ||
containers = r.json() | ||
except Exception as e: | ||
msg = f"Bad container response: {e}" | ||
if r is not None: | ||
msg += ": " + r.text | ||
raise self.failureException(msg) | ||
return path.format(containers[0]["Id"]) | ||
|
||
def assertContainerExists(self, member, msg=None): # pylint: disable=invalid-name | ||
r = requests.get(self.uri(f"/containers/{member}/exists")) | ||
if r.status_code == 404: | ||
if msg is None: | ||
msg = f"Container '{member}' does not exist." | ||
self.failureException(msg) | ||
|
||
def assertContainerNotExists(self, member, msg=None): # pylint: disable=invalid-name | ||
r = requests.get(self.uri(f"/containers/{member}/exists")) | ||
if r.status_code == 204: | ||
if msg is None: | ||
msg = f"Container '{member}' exists." | ||
self.failureException(msg) | ||
|
||
def assertId(self, content): # pylint: disable=invalid-name | ||
objects = json.loads(content) | ||
try: | ||
if isinstance(objects, dict): | ||
_ = objects["Id"] | ||
else: | ||
for item in objects: | ||
_ = item["Id"] | ||
except KeyError: | ||
self.failureException("Failed in find 'Id' in return value.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import random | ||
import unittest | ||
|
||
import requests | ||
from dateutil.parser import parse | ||
|
||
from .fixtures import APITestCase | ||
|
||
|
||
class ContainerTestCase(APITestCase): | ||
def test_list(self): | ||
r = requests.get(self.uri("/containers/json"), timeout=5) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
obj = r.json() | ||
self.assertEqual(len(obj), 0) | ||
|
||
def test_list_all(self): | ||
r = requests.get(self.uri("/containers/json?all=true")) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
self.assertId(r.content) | ||
|
||
def test_inspect(self): | ||
r = requests.get(self.uri(self.resolve_container("/containers/{}/json"))) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
self.assertId(r.content) | ||
_ = parse(r.json()["Created"]) | ||
|
||
def test_stats(self): | ||
r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false"))) | ||
self.assertIn(r.status_code, (200, 409), r.text) | ||
if r.status_code == 200: | ||
self.assertId(r.content) | ||
|
||
def test_delete(self): | ||
r = requests.delete(self.uri(self.resolve_container("/containers/{}"))) | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
def test_stop(self): | ||
r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) | ||
self.assertIn(r.status_code, (204, 304), r.text) | ||
|
||
r = requests.post(self.uri(self.resolve_container("/containers/{}/stop"))) | ||
self.assertIn(r.status_code, (204, 304), r.text) | ||
|
||
def test_start(self): | ||
r = requests.post(self.uri(self.resolve_container("/containers/{}/stop"))) | ||
self.assertIn(r.status_code, (204, 304), r.text) | ||
|
||
r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) | ||
self.assertIn(r.status_code, (204, 304), r.text) | ||
|
||
def test_restart(self): | ||
r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) | ||
self.assertIn(r.status_code, (204, 304), r.text) | ||
|
||
r = requests.post(self.uri(self.resolve_container("/containers/{}/restart")), timeout=5) | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
def test_resize(self): | ||
r = requests.post(self.uri(self.resolve_container("/containers/{}/resize?h=43&w=80"))) | ||
self.assertIn(r.status_code, (200, 409), r.text) | ||
if r.status_code == 200: | ||
self.assertEqual(r.text, "", r.text) | ||
|
||
def test_attach(self): | ||
self.skipTest("FIXME: Test timeouts") | ||
r = requests.post(self.uri(self.resolve_container("/containers/{}/attach")), timeout=5) | ||
self.assertIn(r.status_code, (101, 500), r.text) | ||
|
||
def test_logs(self): | ||
r = requests.get(self.uri(self.resolve_container("/containers/{}/logs?stdout=true"))) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
|
||
def test_commit(self): | ||
r = requests.post(self.uri(self.resolve_container("/commit?container={}"))) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
self.assertId(r.content) | ||
|
||
obj = r.json() | ||
self.assertIsInstance(obj, dict) | ||
|
||
def test_prune(self): | ||
name = f"Container_{random.getrandbits(160):x}" | ||
|
||
r = requests.post( | ||
self.podman_url + f"/v1.40/containers/create?name={name}", | ||
json={ | ||
"Cmd": ["cp", "/etc/motd", "/motd.size_test"], | ||
"Image": "alpine:latest", | ||
"NetworkDisabled": True, | ||
}, | ||
) | ||
self.assertEqual(r.status_code, 201, r.text) | ||
create = r.json() | ||
|
||
r = requests.post(self.podman_url + f"/v1.40/containers/{create['Id']}/start") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
r = requests.post(self.podman_url + f"/v1.40/containers/{create['Id']}/wait") | ||
self.assertEqual(r.status_code, 200, r.text) | ||
wait = r.json() | ||
self.assertEqual(wait["StatusCode"], 0, wait["Error"]["Message"]) | ||
|
||
prune = requests.post(self.podman_url + "/v1.40/containers/prune") | ||
self.assertEqual(prune.status_code, 200, prune.status_code) | ||
prune_payload = prune.json() | ||
self.assertGreater(prune_payload["SpaceReclaimed"], 0) | ||
self.assertIn(create["Id"], prune_payload["ContainersDeleted"]) | ||
|
||
# Delete any orphaned containers | ||
r = requests.get(self.podman_url + "/v1.40/containers/json?all=true") | ||
self.assertEqual(r.status_code, 200, r.text) | ||
for self.resolve_container in r.json(): | ||
requests.delete( | ||
self.podman_url + f"/v1.40/containers/{self.resolve_container['Id']}?force=true" | ||
) | ||
|
||
# Image prune here tied to containers freeing up | ||
prune = requests.post(self.podman_url + "/v1.40/images/prune") | ||
self.assertEqual(prune.status_code, 200, prune.text) | ||
prune_payload = prune.json() | ||
self.assertGreater(prune_payload["SpaceReclaimed"], 0) | ||
|
||
# FIXME need method to determine which image is going to be "pruned" to fix test | ||
# TODO should handler be recursive when deleting images? | ||
# self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) | ||
|
||
# FIXME (@vrothberg): I commented this line out during the `libimage` migration. | ||
# It doesn't make sense to report anything to be deleted if the reclaimed space | ||
# is zero. I think the test needs some rewrite. | ||
# self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) | ||
|
||
def test_status(self): | ||
r = requests.post( | ||
self.podman_url + "/v1.40/containers/create?name=topcontainer", | ||
json={"Cmd": ["top"], "Image": "alpine:latest"}, | ||
) | ||
self.assertEqual(r.status_code, 201, r.text) | ||
payload = r.json() | ||
container_id = payload["Id"] | ||
self.assertIsNotNone(container_id) | ||
|
||
r = requests.get( | ||
self.podman_url + "/v1.40/containers/json", | ||
params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, | ||
) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
payload = r.json() | ||
self.assertEqual(payload[0]["Status"], "Created") | ||
|
||
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
r = requests.get( | ||
self.podman_url + "/v1.40/containers/json", | ||
params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, | ||
) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
payload = r.json() | ||
self.assertTrue(str(payload[0]["Status"]).startswith("Up")) | ||
|
||
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/pause") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
r = requests.get( | ||
self.podman_url + "/v1.40/containers/json", | ||
params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, | ||
) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
payload = r.json() | ||
self.assertTrue(str(payload[0]["Status"]).startswith("Up")) | ||
self.assertTrue(str(payload[0]["Status"]).endswith("(Paused)")) | ||
|
||
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/unpause") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/stop") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
r = requests.get( | ||
self.podman_url + "/v1.40/containers/json", | ||
params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, | ||
) | ||
self.assertEqual(r.status_code, 200, r.text) | ||
payload = r.json() | ||
self.assertTrue(str(payload[0]["Status"]).startswith("Exited")) | ||
|
||
r = requests.delete(self.podman_url + f"/v1.40/containers/{container_id}") | ||
self.assertEqual(r.status_code, 204, r.text) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Oops, something went wrong.