-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(image): introduce DockerImage class for flexible image handling
Redefines the approach to building and managing Docker images within testcontainers by introducing the DockerImage class. This class encapsulates the logic for building images from Dockerfiles and pulling images from repositories. Key Changes: - Implements DockerImage as a central class for image operations, including build, pull, get, and remove. - Add this class as acceptable image param type for DockerContainer and DockerClient - Enables direct Dockerfile support while preserving the option to pull existing images, facilitating a more dynamic testing setup. This refactor addresses feedback on the initial implementation, proposing a cleaner, more extensible design.
- Loading branch information
1 parent
87b5873
commit 2dfe8a6
Showing
6 changed files
with
167 additions
and
5 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
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
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,115 @@ | ||
import functools as ft | ||
from typing import Optional | ||
|
||
import docker | ||
from docker.client import DockerClient | ||
from docker.models.images import Image, ImageCollection | ||
|
||
from .utils import setup_logger | ||
|
||
LOGGER = setup_logger(__name__) | ||
|
||
|
||
class DockerImage: | ||
""" | ||
Basic class to manage docker images. | ||
.. doctest:: | ||
>>> from testcontainers.core.image import DockerImage | ||
>>> image = DockerImage().from_dockerfile(path="core/tests/", tag="testcontainers/test-image") | ||
>>> image.exists("testcontainers/test-image") | ||
True | ||
>>> image.get("testcontainers/test-image").id | ||
'sha256:...' | ||
>>> image.remove(force=True) | ||
>>> image.exists("testcontainers/test-image") | ||
False | ||
""" | ||
|
||
def __init__(self, docker_client_kw: Optional[dict] = None, **kwargs) -> None: | ||
self._docker = DockerClient().from_env(**(docker_client_kw or {})) | ||
|
||
def from_dockerfile(self, path: str, tag: str = "local/image") -> "DockerImage": | ||
""" | ||
Build an image from a Dockerfile. | ||
Args: | ||
path (str): Path to the Dockerfile | ||
tag (str): Tag for the image | ||
Returns: | ||
DockerImage: The current instance | ||
""" | ||
self.build(path=path, tag=tag) | ||
return self | ||
|
||
def from_image(self, repository: str, tag: str = "latest") -> "DockerImage": | ||
""" | ||
Pull an image from the registry. | ||
Args: | ||
repository (str): Image repository | ||
tag (str): Image tag | ||
Returns: | ||
DockerImage: The current instance | ||
""" | ||
self.pull(repository=repository, tag=tag) | ||
return self | ||
|
||
@ft.wraps(ImageCollection.build) | ||
def build(self, **kwargs) -> "DockerImage": | ||
LOGGER.info("Building image from Dockerfile") | ||
self._image, _ = self._docker.images.build(**kwargs) | ||
return self | ||
|
||
@ft.wraps(ImageCollection.pull) | ||
def pull(self, **kwargs) -> Image: | ||
LOGGER.info("Pulling image") | ||
self._image = self._docker.images.pull(**kwargs) | ||
return self | ||
|
||
@ft.wraps(ImageCollection.get) | ||
def get(self, image: str) -> Image: | ||
LOGGER.info(f"Getting image {image}") | ||
image_obj = self._docker.images.get(image) | ||
return image_obj | ||
|
||
@ft.wraps(ImageCollection.remove) | ||
def remove(self, **kwargs) -> None: | ||
LOGGER.info(f"Removing image {self._image}") | ||
self._image.remove(**kwargs) | ||
|
||
@property | ||
def id(self) -> str: | ||
return self._image.id | ||
|
||
@property | ||
def short_id(self) -> str: | ||
return self._image.short_id | ||
|
||
@property | ||
def tags(self) -> dict: | ||
return self._image.tags | ||
|
||
def get_wrapped_image(self) -> Image: | ||
return self._image | ||
|
||
def exists(self, image: str) -> bool: | ||
""" | ||
Check if the image exists in the local registry. | ||
Args: | ||
image (str): Image name | ||
Returns: | ||
bool: True if the image exists, False otherwise | ||
Raises: | ||
docker.errors.ImageNotFound: If the image does not exist | ||
""" | ||
try: | ||
self.get(image) | ||
return True | ||
except docker.errors.ImageNotFound: | ||
return False |
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,34 @@ | ||
import pytest | ||
from testcontainers.core.image import DockerImage | ||
|
||
|
||
def test_docker_image_from_dockerfile(): | ||
image = DockerImage().from_dockerfile(path="core/tests/", tag="testcontainers/test-image") | ||
|
||
assert image.exists("testcontainers/test-image") == True | ||
|
||
retrieved_image = image.get("testcontainers/test-image") | ||
|
||
assert retrieved_image.id == image.id | ||
assert retrieved_image.short_id == image.short_id | ||
assert retrieved_image.tags == image.tags | ||
|
||
image.remove(force=True) | ||
|
||
assert image.exists("testcontainers/test-image") == False | ||
|
||
|
||
def test_docker_image_from_image(): | ||
image = DockerImage().from_image(repository="alpine") | ||
|
||
assert image.exists("alpine") == True | ||
|
||
retrieved_image = image.get("alpine") | ||
|
||
assert retrieved_image.id == image.id | ||
assert retrieved_image.short_id == image.short_id | ||
assert retrieved_image.tags == image.tags | ||
|
||
image.remove(force=True) | ||
|
||
assert image.exists("alpine") == False |