Skip to content

Commit

Permalink
Merge pull request containers#10372 from jwhonce/issues/9238
Browse files Browse the repository at this point in the history
Break up python APIv2 tests
  • Loading branch information
openshift-merge-robot authored May 18, 2021
2 parents d1d21f0 + 98955be commit 353f04b
Show file tree
Hide file tree
Showing 15 changed files with 865 additions and 749 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ remotesystem:
.PHONY: localapiv2
localapiv2:
env PODMAN=./bin/podman ./test/apiv2/test-apiv2
env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/rest_api/
env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/python
env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/python/docker

.PHONY: remoteapiv2
Expand Down
Empty file added test/apiv2/python/__init__.py
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions test/apiv2/python/rest_api/fixtures/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .api_testcase import APITestCase

__all__ = ["APITestCase"]
103 changes: 103 additions & 0 deletions test/apiv2/python/rest_api/fixtures/api_testcase.py
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.")
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import tempfile


class Podman(object):
class Podman:
"""
Instances hold the configuration and setup for running podman commands
"""
Expand All @@ -34,7 +34,7 @@ def __init__(self):
p = configparser.ConfigParser()
p.read_dict(
{
"registries.search": {"registries": "['docker.io']"},
"registries.search": {"registries": "['quay.io']"},
"registries.insecure": {"registries": "[]"},
"registries.block": {"registries": "[]"},
}
Expand Down Expand Up @@ -102,7 +102,7 @@ def open(self, command, *args, **kwargs):
)

def run(self, command, *args, **kwargs):
"""Podman initialized instance to run a given command
"""Run given podman command
:param self: Podman instance
:param command: podman sub-command to run
Expand Down
192 changes: 192 additions & 0 deletions test/apiv2/python/rest_api/test_v2_0_0_container.py
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()
Loading

0 comments on commit 353f04b

Please sign in to comment.