Refactor docker-py compatibility tests

* Add which python client is being used to run tests, see "python
  client" below.
* Remove redundate code from test classes
* Update/Add comments to modules and classes

======================================================= test session starts ========================================================
platform linux -- Python 3.10.0, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
python client -- DockerClient
rootdir: /home/jhonce/Projects/go/src/github.com/containers/podman
plugins: requests-mock-1.8.0
collected 33 items

test/python/docker/compat/test_containers.py ...s..............  [ 54%]
test/python/docker/compat/test_images.py ............  [ 90%]
test/python/docker/compat/test_system.py ...  [100%]

Note: Follow-up PRs will verify the test results and expand the tests.

Signed-off-by: Jhon Honce <jhonce@redhat.com>
pull/13380/head
Jhon Honce 2022-02-28 16:26:43 -07:00
parent c39dffe83d
commit 7729afe979
9 changed files with 275 additions and 299 deletions

View File

@ -540,7 +540,7 @@ validate.completions:
.PHONY: run-docker-py-tests
run-docker-py-tests:
touch test/__init__.py
env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf pytest test/python/docker/
env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf pytest --disable-warnings test/python/docker/
-rm test/__init__.py
.PHONY: localunit

15
pyproject.toml Normal file
View File

@ -0,0 +1,15 @@
[tool.black]
line-length = 100
target-version = ['py36']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.tox
| \.venv
| build
| dist
| docs
| hack
)/
'''

View File

@ -1,4 +1,6 @@
import configparser
"""
Helpers for integration tests using DockerClient
"""
import json
import os
import pathlib
@ -11,7 +13,7 @@ from docker import DockerClient
from .compat import constant
class Podman(object):
class PodmanAPI:
"""
Instances hold the configuration and setup for running podman commands
"""
@ -53,17 +55,13 @@ location = "mirror.localhost:5000"
"""
with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as w:
w.write(conf)
with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as file:
file.write(conf)
os.environ["CNI_CONFIG_PATH"] = os.path.join(
self.anchor_directory, "cni", "net.d"
)
os.environ["CNI_CONFIG_PATH"] = os.path.join(self.anchor_directory, "cni", "net.d")
os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True)
self.cmd.append("--network-config-dir=" + os.environ["CNI_CONFIG_PATH"])
cni_cfg = os.path.join(
os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist"
)
cni_cfg = os.path.join(os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist")
# json decoded and encoded to ensure legal json
buf = json.loads(
"""
@ -93,8 +91,8 @@ location = "mirror.localhost:5000"
}
"""
)
with open(cni_cfg, "w") as w:
json.dump(buf, w)
with open(cni_cfg, "w") as file:
json.dump(buf, file)
def open(self, command, *args, **kwargs):
"""Podman initialized instance to run a given command
@ -111,6 +109,7 @@ location = "mirror.localhost:5000"
shell = kwargs.get("shell", False)
# pylint: disable=consider-using-with
return subprocess.Popen(
cmd,
shell=shell,
@ -144,9 +143,11 @@ location = "mirror.localhost:5000"
)
def tear_down(self):
"""Delete test environment."""
shutil.rmtree(self.anchor_directory, ignore_errors=True)
def restore_image_from_cache(self, client: DockerClient):
"""Populate images from cache."""
path = os.path.join(self.image_cache, constant.ALPINE_TARBALL)
if not os.path.exists(path):
img = client.images.pull(constant.ALPINE)
@ -157,5 +158,6 @@ location = "mirror.localhost:5000"
self.run("load", "-i", path, check=True)
def flush_image_cache(self):
for f in pathlib.Path(self.image_cache).glob("*.tar"):
f.unlink(f)
"""Delete image cache."""
for file in pathlib.Path(self.image_cache).glob("*.tar"):
file.unlink(missing_ok=True)

View File

@ -1,23 +1,92 @@
"""
Fixtures and Helpers for unittests.
"""
import subprocess
import sys
import time
import unittest
# pylint: disable=no-name-in-module,import-error,wrong-import-order
from docker import DockerClient
from test.python.docker import PodmanAPI
from test.python.docker.compat import constant
def run_top_container(client: DockerClient):
c = client.containers.create(
constant.ALPINE, command="top", detach=True, tty=True, name="top"
"""Run top command in a alpine container."""
ctnr = client.containers.create(
constant.ALPINE,
command="top",
detach=True,
tty=True,
name="top",
)
c.start()
return c.id
ctnr.start()
return ctnr.id
def remove_all_containers(client: DockerClient):
"""Delete all containers from the Podman service."""
for ctnr in client.containers.list(all=True):
ctnr.remove(force=True)
def remove_all_images(client: DockerClient):
"""Delete all images from the Podman service."""
for img in client.images.list():
# FIXME should DELETE /images accept the sha256: prefix?
id_ = img.id.removeprefix("sha256:")
client.images.remove(id_, force=True)
class DockerTestCase(unittest.TestCase):
"""Specialized TestCase class for testing against Podman service."""
podman: PodmanAPI = None # initialized podman configuration for tests
service: subprocess.Popen = None # podman service instance
top_container_id: str = None
docker: DockerClient = None
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.podman = PodmanAPI()
super().addClassCleanup(cls.podman.tear_down)
cls.service = cls.podman.open("system", "service", "tcp:127.0.0.1:8080", "--time=0")
# give the service some time to be ready...
time.sleep(2)
return_code = cls.service.poll()
if return_code is not None:
raise subprocess.CalledProcessError(return_code, "podman system service")
@classmethod
def tearDownClass(cls) -> None:
cls.service.terminate()
stdout, stderr = cls.service.communicate(timeout=0.5)
if stdout:
sys.stdout.write("\ndocker-py -- Service Stdout:\n" + stdout.decode("utf-8"))
if stderr:
sys.stderr.write("\ndocker-py -- Service Stderr:\n" + stderr.decode("utf-8"))
return super().tearDownClass()
def setUp(self) -> None:
super().setUp()
self.docker = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15)
self.addCleanup(self.docker.close)
self.podman.restore_image_from_cache(self.docker)
self.top_container_id = run_top_container(self.docker)
self.assertIsNotNone(self.top_container_id, "Failed to create 'top' container")
def tearDown(self) -> None:
remove_all_containers(self.docker)
remove_all_images(self.docker)
super().tearDown()

View File

@ -1,6 +1,8 @@
"""
Constants to use in writing unittests.
"""
ALPINE = "quay.io/libpod/alpine:latest"
ALPINE_SHORTNAME = "alpine"
ALPINE_TARBALL = "alpine.tar"
BB = "quay.io/libpod/busybox:latest"
NGINX = "quay.io/libpod/alpine_nginx:latest"
infra = "k8s.gcr.io/pause:3.2"

View File

@ -1,111 +1,56 @@
"""
Integration tests for exercising docker-py against Podman Service.
"""
import io
import subprocess
import sys
import time
import unittest
from typing import IO, Optional, List
import tarfile
from typing import IO, List, Optional
from docker import DockerClient, errors
from docker import errors
from docker.models.containers import Container
from docker.models.images import Image
from docker.models.volumes import Volume
from docker.types import Mount
from test.python.docker import Podman
# pylint: disable=no-name-in-module,import-error,wrong-import-order
from test.python.docker.compat import common, constant
import tarfile
class TestContainers(unittest.TestCase):
podman = None # initialized podman configuration for tests
service = None # podman service instance
topContainerId = ""
def setUp(self):
super().setUp()
self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15)
TestContainers.podman.restore_image_from_cache(self.client)
TestContainers.topContainerId = common.run_top_container(self.client)
self.assertIsNotNone(TestContainers.topContainerId)
def tearDown(self):
common.remove_all_containers(self.client)
common.remove_all_images(self.client)
self.client.close()
return super().tearDown()
@classmethod
def setUpClass(cls):
super().setUpClass()
TestContainers.podman = Podman()
TestContainers.service = TestContainers.podman.open(
"system", "service", "tcp:127.0.0.1:8080", "--time=0"
)
# give the service some time to be ready...
time.sleep(2)
rc = TestContainers.service.poll()
if rc is not None:
raise subprocess.CalledProcessError(rc, "podman system service")
@classmethod
def tearDownClass(cls):
TestContainers.service.terminate()
stdout, stderr = TestContainers.service.communicate(timeout=0.5)
if stdout:
sys.stdout.write("\nContainers Service Stdout:\n" + stdout.decode("utf-8"))
if stderr:
sys.stderr.write("\nContainers Service Stderr:\n" + stderr.decode("utf-8"))
TestContainers.podman.tear_down()
return super().tearDownClass()
# pylint: disable=missing-function-docstring
class TestContainers(common.DockerTestCase):
"""TestCase for exercising containers."""
def test_create_container(self):
# Run a container with detach mode
self.client.containers.create(image="alpine", detach=True)
self.assertEqual(len(self.client.containers.list(all=True)), 2)
"""Run a container with detach mode."""
self.docker.containers.create(image="alpine", detach=True)
self.assertEqual(len(self.docker.containers.list(all=True)), 2)
def test_create_network(self):
net = self.client.networks.create("testNetwork", driver="bridge")
ctnr = self.client.containers.create(image="alpine", detach=True)
# TODO fix when ready
# This test will not work until all connect|disconnect
# code is fixed.
# net.connect(ctnr)
# nets = self.client.networks.list(greedy=True)
# self.assertGreaterEqual(len(nets), 1)
# TODO fix endpoint to include containers
# for n in nets:
# if n.id == "testNetwork":
# self.assertEqual(ctnr.id, n.containers)
# self.assertTrue(False, "testNetwork not found")
"""Add network to a container."""
self.docker.networks.create("testNetwork", driver="bridge")
self.docker.containers.create(image="alpine", detach=True)
def test_start_container(self):
# Podman docs says it should give a 304 but returns with no response
# # Start a already started container should return 304
# response = self.client.api.start(container=TestContainers.topContainerId)
# response = self.docker.api.start(container=self.top_container_id)
# self.assertEqual(error.exception.response.status_code, 304)
# Create a new container and validate the count
self.client.containers.create(image=constant.ALPINE, name="container2")
containers = self.client.containers.list(all=True)
self.docker.containers.create(image=constant.ALPINE, name="container2")
containers = self.docker.containers.list(all=True)
self.assertEqual(len(containers), 2)
def test_start_container_with_random_port_bind(self):
container = self.client.containers.create(
container = self.docker.containers.create(
image=constant.ALPINE,
name="containerWithRandomBind",
ports={"1234/tcp": None},
)
containers = self.client.containers.list(all=True)
containers = self.docker.containers.list(all=True)
self.assertTrue(container in containers)
def test_stop_container(self):
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
self.assertEqual(top.status, "running")
# Stop a running container and validate the state
@ -114,7 +59,7 @@ class TestContainers(unittest.TestCase):
self.assertIn(top.status, ("stopped", "exited"))
def test_kill_container(self):
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
self.assertEqual(top.status, "running")
# Kill a running container and validate the state
@ -124,7 +69,7 @@ class TestContainers(unittest.TestCase):
def test_restart_container(self):
# Validate the container state
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
top.stop()
top.reload()
self.assertIn(top.status, ("stopped", "exited"))
@ -136,16 +81,16 @@ class TestContainers(unittest.TestCase):
def test_remove_container(self):
# Remove container by ID with force
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
top.remove(force=True)
self.assertEqual(len(self.client.containers.list()), 0)
self.assertEqual(len(self.docker.containers.list()), 0)
def test_remove_container_without_force(self):
# Validate current container count
self.assertEqual(len(self.client.containers.list()), 1)
self.assertEqual(len(self.docker.containers.list()), 1)
# Remove running container should throw error
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
with self.assertRaises(errors.APIError) as error:
top.remove()
self.assertEqual(error.exception.response.status_code, 500)
@ -153,11 +98,11 @@ class TestContainers(unittest.TestCase):
# Remove container by ID without force
top.stop()
top.remove()
self.assertEqual(len(self.client.containers.list()), 0)
self.assertEqual(len(self.docker.containers.list()), 0)
def test_pause_container(self):
# Validate the container state
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
self.assertEqual(top.status, "running")
# Pause a running container and validate the state
@ -167,7 +112,7 @@ class TestContainers(unittest.TestCase):
def test_pause_stopped_container(self):
# Stop the container
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
top.stop()
# Pause exited container should throw error
@ -176,7 +121,7 @@ class TestContainers(unittest.TestCase):
self.assertEqual(error.exception.response.status_code, 500)
def test_unpause_container(self):
top = self.client.containers.get(TestContainers.topContainerId)
top = self.docker.containers.get(self.top_container_id)
# Validate the container state
top.pause()
@ -190,21 +135,21 @@ class TestContainers(unittest.TestCase):
def test_list_container(self):
# Add container and validate the count
self.client.containers.create(image="alpine", detach=True)
containers = self.client.containers.list(all=True)
self.docker.containers.create(image="alpine", detach=True)
containers = self.docker.containers.list(all=True)
self.assertEqual(len(containers), 2)
def test_filters(self):
self.skipTest("TODO Endpoint does not yet support filters")
# List container with filter by id
filters = {"id": TestContainers.topContainerId}
ctnrs = self.client.containers.list(all=True, filters=filters)
filters = {"id": self.top_container_id}
ctnrs = self.docker.containers.list(all=True, filters=filters)
self.assertEqual(len(ctnrs), 1)
# List container with filter by name
filters = {"name": "top"}
ctnrs = self.client.containers.list(all=True, filters=filters)
ctnrs = self.docker.containers.list(all=True, filters=filters)
self.assertEqual(len(ctnrs), 1)
def test_copy_to_container(self):
@ -212,31 +157,33 @@ class TestContainers(unittest.TestCase):
vol: Optional[Volume] = None
try:
test_file_content = b"Hello World!"
vol = self.client.volumes.create("test-volume")
ctr = self.client.containers.create(image="alpine",
detach=True,
command="top",
volumes=["test-volume:/test-volume-read-only:ro"])
vol = self.docker.volumes.create("test-volume")
ctr = self.docker.containers.create(
image="alpine",
detach=True,
command="top",
volumes=["test-volume:/test-volume-read-only:ro"],
)
ctr.start()
buff: IO[bytes] = io.BytesIO()
with tarfile.open(fileobj=buff, mode="w:") as tf:
ti: tarfile.TarInfo = tarfile.TarInfo()
ti.uid = 1042
ti.gid = 1043
ti.name = "a.txt"
ti.path = "a.txt"
ti.mode = 0o644
ti.type = tarfile.REGTYPE
ti.size = len(test_file_content)
tf.addfile(ti, fileobj=io.BytesIO(test_file_content))
with tarfile.open(fileobj=buff, mode="w:") as file:
info: tarfile.TarInfo = tarfile.TarInfo()
info.uid = 1042
info.gid = 1043
info.name = "a.txt"
info.path = "a.txt"
info.mode = 0o644
info.type = tarfile.REGTYPE
info.size = len(test_file_content)
file.addfile(info, fileobj=io.BytesIO(test_file_content))
buff.seek(0)
ctr.put_archive("/tmp/", buff)
ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/tmp/a.txt"])
self.assertEqual(ret, 0)
self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID of copied file")
self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID of copied file")
ret, out = ctr.exec_run(["cat", "/tmp/a.txt"])
self.assertEqual(ret, 0)
@ -253,48 +200,61 @@ class TestContainers(unittest.TestCase):
vol.remove(force=True)
def test_mount_preexisting_dir(self):
dockerfile = (B'FROM quay.io/libpod/alpine:latest\n'
B'USER root\n'
B'RUN mkdir -p /workspace\n'
B'RUN chown 1042:1043 /workspace')
dockerfile = (
b"FROM quay.io/libpod/alpine:latest\n"
b"USER root\n"
b"RUN mkdir -p /workspace\n"
b"RUN chown 1042:1043 /workspace"
)
img: Image
img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile))
ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top",
volumes=["test_mount_preexisting_dir_vol:/workspace"])
img, out = self.docker.images.build(fileobj=io.BytesIO(dockerfile))
ctr: Container = self.docker.containers.create(
image=img.id,
detach=True,
command="top",
volumes=["test_mount_preexisting_dir_vol:/workspace"],
)
ctr.start()
ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"])
self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID set in dockerfile")
_, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"])
self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID set in dockerfile")
def test_non_existant_workdir(self):
dockerfile = (B'FROM quay.io/libpod/alpine:latest\n'
B'USER root\n'
B'WORKDIR /workspace/scratch\n'
B'RUN touch test')
dockerfile = (
b"FROM quay.io/libpod/alpine:latest\n"
b"USER root\n"
b"WORKDIR /workspace/scratch\n"
b"RUN touch test"
)
img: Image
img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile))
ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top",
volumes=["test_non_existant_workdir:/workspace"])
img, _ = self.docker.images.build(fileobj=io.BytesIO(dockerfile))
ctr: Container = self.docker.containers.create(
image=img.id,
detach=True,
command="top",
volumes=["test_non_existant_workdir:/workspace"],
)
ctr.start()
ret, out = ctr.exec_run(["stat", "/workspace/scratch/test"])
ret, _ = ctr.exec_run(["stat", "/workspace/scratch/test"])
self.assertEqual(ret, 0, "Working directory created if it doesn't exist")
def test_mount_rw_by_default(self):
ctr: Optional[Container] = None
vol: Optional[Volume] = None
try:
vol = self.client.volumes.create("test-volume")
ctr = self.client.containers.create(image="alpine",
detach=True,
command="top",
mounts=[Mount(target="/vol-mnt",
source="test-volume",
type='volume',
read_only=False)])
ctr_inspect = self.client.api.inspect_container(ctr.id)
vol = self.docker.volumes.create("test-volume")
ctr = self.docker.containers.create(
image="alpine",
detach=True,
command="top",
mounts=[
Mount(target="/vol-mnt", source="test-volume", type="volume", read_only=False)
],
)
ctr_inspect = self.docker.api.inspect_container(ctr.id)
binds: List[str] = ctr_inspect["HostConfig"]["Binds"]
self.assertEqual(len(binds), 1)
self.assertEqual(binds[0], 'test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind')
self.assertEqual(binds[0], "test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind")
finally:
if ctr is not None:
ctr.remove()

View File

@ -1,116 +1,73 @@
import collections
"""
Integration tests for exercising docker-py against Podman Service.
"""
import io
import os
import subprocess
import sys
import time
import unittest
from docker import DockerClient, errors
from docker.errors import APIError
from docker import errors
from test.python.docker import Podman
# pylint: disable=no-name-in-module,import-error,wrong-import-order
from test.python.docker.compat import common, constant
class TestImages(unittest.TestCase):
podman = None # initialized podman configuration for tests
service = None # podman service instance
def setUp(self):
super().setUp()
self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15)
TestImages.podman.restore_image_from_cache(self.client)
def tearDown(self):
common.remove_all_images(self.client)
self.client.close()
return super().tearDown()
@classmethod
def setUpClass(cls):
super().setUpClass()
TestImages.podman = Podman()
TestImages.service = TestImages.podman.open(
"system", "service", "tcp:127.0.0.1:8080", "--time=0"
)
# give the service some time to be ready...
time.sleep(2)
returncode = TestImages.service.poll()
if returncode is not None:
raise subprocess.CalledProcessError(returncode, "podman system service")
@classmethod
def tearDownClass(cls):
TestImages.service.terminate()
stdout, stderr = TestImages.service.communicate(timeout=0.5)
if stdout:
sys.stdout.write("\nImages Service Stdout:\n" + stdout.decode("utf-8"))
if stderr:
sys.stderr.write("\nImAges Service Stderr:\n" + stderr.decode("utf-8"))
TestImages.podman.tear_down()
return super().tearDownClass()
class TestImages(common.DockerTestCase):
"""TestCase for exercising images."""
def test_tag_valid_image(self):
"""Validates if the image is tagged successfully"""
alpine = self.client.images.get(constant.ALPINE)
alpine = self.docker.images.get(constant.ALPINE)
self.assertTrue(alpine.tag("demo", constant.ALPINE_SHORTNAME))
alpine = self.client.images.get(constant.ALPINE)
for t in alpine.tags:
self.assertIn("alpine", t)
alpine = self.docker.images.get(constant.ALPINE)
for tag in alpine.tags:
self.assertIn("alpine", tag)
# @unittest.skip("doesn't work now")
def test_retag_valid_image(self):
"""Validates if name updates when the image is retagged"""
alpine = self.client.images.get(constant.ALPINE)
"""Validates if name updates when the image is re-tagged."""
alpine = self.docker.images.get(constant.ALPINE)
self.assertTrue(alpine.tag("demo", "rename"))
alpine = self.client.images.get(constant.ALPINE)
alpine = self.docker.images.get(constant.ALPINE)
self.assertNotIn("demo:test", alpine.tags)
def test_list_images(self):
"""List images"""
self.assertEqual(len(self.client.images.list()), 1)
self.assertEqual(len(self.docker.images.list()), 1)
# Add more images
self.client.images.pull(constant.BB)
self.assertEqual(len(self.client.images.list()), 2)
self.assertEqual(len(self.client.images.list(all=True)), 2)
self.docker.images.pull(constant.BB)
self.assertEqual(len(self.docker.images.list()), 2)
self.assertEqual(len(self.docker.images.list(all=True)), 2)
# List images with filter
self.assertEqual(len(self.client.images.list(filters={"reference": "alpine"})), 1)
self.assertEqual(len(self.docker.images.list(filters={"reference": "alpine"})), 1)
def test_search_image(self):
"""Search for image"""
for r in self.client.images.search("alpine"):
for registry in self.docker.images.search("alpine"):
# registry matches if string is in either one
self.assertIn("alpine", r["Name"]+" "+r["Description"].lower())
self.assertIn("alpine", registry["Name"] + " " + registry["Description"].lower())
def test_search_bogus_image(self):
"""Search for bogus image should throw exception"""
try:
r = self.client.images.search("bogus/bogus")
except:
return
self.assertTrue(len(r) == 0)
with self.assertRaises(errors.APIError):
self.docker.images.search("bogus/bogus")
def test_remove_image(self):
"""Remove image"""
# Check for error with wrong image name
with self.assertRaises(errors.NotFound):
self.client.images.remove("dummy")
self.assertEqual(len(self.client.images.list()), 1)
self.docker.images.remove("dummy")
self.client.images.remove(constant.ALPINE)
self.assertEqual(len(self.client.images.list()), 0)
common.remove_all_containers(self.docker)
self.assertEqual(len(self.docker.images.list()), 1)
self.docker.images.remove(constant.ALPINE)
self.assertEqual(len(self.docker.images.list()), 0)
def test_image_history(self):
"""Image history"""
img = self.client.images.get(constant.ALPINE)
img = self.docker.images.get(constant.ALPINE)
history = img.history()
image_id = img.id[7:] if img.id.startswith("sha256:") else img.id
@ -122,47 +79,49 @@ class TestImages(unittest.TestCase):
def test_get_image_exists_not(self):
"""Negative test for get image"""
with self.assertRaises(errors.NotFound):
response = self.client.images.get("image_does_not_exists")
collections.deque(response)
self.docker.images.get("image_does_not_exists")
def test_save_image(self):
"""Export Image"""
image = self.client.images.pull(constant.BB)
image = self.docker.images.pull(constant.BB)
file = os.path.join(TestImages.podman.image_cache, "busybox.tar")
with open(file, mode="wb") as tarball:
for frame in image.save(named=True):
tarball.write(frame)
sz = os.path.getsize(file)
self.assertGreater(sz, 0)
self.assertGreater(os.path.getsize(file), 0)
def test_load_image(self):
"""Import|Load Image"""
self.assertEqual(len(self.client.images.list()), 1)
self.assertEqual(len(self.docker.images.list()), 1)
image = self.client.images.pull(constant.BB)
image = self.docker.images.pull(constant.BB)
file = os.path.join(TestImages.podman.image_cache, "busybox.tar")
with open(file, mode="wb") as tarball:
for frame in image.save():
tarball.write(frame)
with open(file, mode="rb") as saved:
_ = self.client.images.load(saved)
self.docker.images.load(saved)
self.assertEqual(len(self.client.images.list()), 2)
self.assertEqual(len(self.docker.images.list()), 2)
def test_load_corrupt_image(self):
"""Import|Load Image failure"""
tarball = io.BytesIO("This is a corrupt tarball".encode("utf-8"))
with self.assertRaises(APIError):
self.client.images.load(tarball)
with self.assertRaises(errors.APIError):
self.docker.images.load(tarball)
def test_build_image(self):
"""Build Image with custom labels."""
labels = {"apple": "red", "grape": "green"}
_ = self.client.images.build(
path="test/python/docker/build_labels", labels=labels, tag="labels", isolation="default"
self.docker.images.build(
path="test/python/docker/build_labels",
labels=labels,
tag="labels",
isolation="default",
)
image = self.client.images.get("labels")
image = self.docker.images.get("labels")
self.assertEqual(image.labels["apple"], labels["apple"])
self.assertEqual(image.labels["grape"], labels["grape"])

View File

@ -1,71 +1,32 @@
import subprocess
import sys
import time
import unittest
"""
Integration tests for exercising docker-py against Podman Service.
"""
from docker import DockerClient
from test.python.docker import Podman, constant
# pylint: disable=no-name-in-module,import-error,wrong-import-order
from test.python.docker import constant
from test.python.docker.compat import common
class TestSystem(unittest.TestCase):
podman = None # initialized podman configuration for tests
service = None # podman service instance
topContainerId = ""
# pylint: disable=missing-function-docstring
class TestSystem(common.DockerTestCase):
"""TestCase for exercising Podman system services."""
def setUp(self):
super().setUp()
self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15)
TestSystem.podman.restore_image_from_cache(self.client)
TestSystem.topContainerId = common.run_top_container(self.client)
def tearDown(self):
common.remove_all_containers(self.client)
common.remove_all_images(self.client)
self.client.close()
return super().tearDown()
@classmethod
def setUpClass(cls):
super().setUpClass()
TestSystem.podman = Podman()
TestSystem.service = TestSystem.podman.open(
"system", "service", "tcp:127.0.0.1:8080", "--time=0"
)
# give the service some time to be ready...
time.sleep(2)
returncode = TestSystem.service.poll()
if returncode is not None:
raise subprocess.CalledProcessError(returncode, "podman system service")
@classmethod
def tearDownClass(cls):
TestSystem.service.terminate()
stdout, stderr = TestSystem.service.communicate(timeout=0.5)
if stdout:
sys.stdout.write("\nImages Service Stdout:\n" + stdout.decode("utf-8"))
if stderr:
sys.stderr.write("\nImAges Service Stderr:\n" + stderr.decode("utf-8"))
TestSystem.podman.tear_down()
return super().tearDownClass()
def test_Info(self):
info = self.client.info()
def test_info(self):
info = self.docker.info()
self.assertIsNotNone(info)
self.assertEqual(info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Secure"], False)
self.assertEqual(info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Mirrors"], ["mirror.localhost:5000"])
self.assertEqual(
info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Mirrors"],
["mirror.localhost:5000"],
)
def test_info_container_details(self):
info = self.client.info()
info = self.docker.info()
self.assertEqual(info["Containers"], 1)
self.client.containers.create(image=constant.ALPINE)
info = self.client.info()
self.docker.containers.create(image=constant.ALPINE)
info = self.docker.info()
self.assertEqual(info["Containers"], 2)
def test_version(self):
version = self.client.version()
version = self.docker.version()
self.assertIsNotNone(version["Platform"]["Name"])

View File

@ -0,0 +1,8 @@
"""
Configure pytest
"""
def pytest_report_header(config):
"""Add header to report."""
return "python client -- DockerClient"