-
Notifications
You must be signed in to change notification settings - Fork 5
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 #22 from touero/develop
Update some docs and from setup.py turn to pyproject.toml
- Loading branch information
Showing
7 changed files
with
168 additions
and
147 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
.gitignore | ||
pyproject.toml | ||
.idea/ | ||
__pycache__/ | ||
dist/ | ||
easier_docker.egg-info/ | ||
example.egg-info/ | ||
venv/ | ||
build/ | ||
.coverage | ||
.coverage | ||
htmlcov | ||
easierdocker/easier_docker.egg-info/ |
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 |
---|---|---|
@@ -1,15 +1,31 @@ | ||
.PHONY: test clean all | ||
.PHONY: test clean build upload all | ||
|
||
all: test clean | ||
TWINE_UPLOAD := twine upload --repository pypi --username __token__ --password $(TWINE_API_TOKEN) | ||
|
||
all: clean build | ||
|
||
test: | ||
coverage run -m unittest discover | ||
coverage report | ||
coverage html | ||
google-chrome htmlcov/index.html | ||
|
||
|
||
clean: | ||
find . -name '__pycache__' -type d -exec rm -rf {} + | ||
rm -rf build/* | ||
rm -rf dist/* | ||
rm -rf dist/* | ||
rm -rf easier_docker.egg-info/* | ||
find . -name 'easier_docker.egg-info' -type d -exec rm -rf {} + | ||
rm -rf build | ||
rm -rf dist | ||
rm -rf .coverage | ||
rm -rf htmlcov | ||
|
||
build: | ||
python -m build | ||
|
||
upload: | ||
@echo "Uploading the package..." | ||
@if [ -z "$(TWINE_API_TOKEN)" ]; then \ | ||
echo "Error: TWINE_API_TOKEN is not set. Please export it as an environment variable."; \ | ||
exit 1; \ | ||
fi | ||
$(TWINE_UPLOAD) dist/* |
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,37 @@ | ||
[build-system] | ||
requires = ["setuptools>=68.2.0", "wheel>=0.42.0"] | ||
build-backend = "setuptools.build_meta" | ||
|
||
|
||
[tool.setuptools.packages] | ||
find = { where = ["easierdocker"] } | ||
|
||
|
||
[project] | ||
name = "easier-docker" | ||
version = "2.2.4" | ||
description = "Configure your container image information more easily in python, allowing the container in docker to execute the configured program you want to execute." | ||
readme = "README.md" | ||
requires-python = ">=3.8" | ||
license = {text = "Apache License 2.0"} | ||
authors = [ | ||
{name = "EnSong Wei", email = "[email protected]"} | ||
] | ||
|
||
|
||
[project.urls] | ||
Homepage = "https://github.com/touero/easier-docker" | ||
"Bug Reports" = "https://github.com/touero/easier-docker/issues" | ||
Source = "https://github.com/touero/easier-docker" | ||
|
||
|
||
keywords = "easy, docker, docker sdk, python docker" | ||
|
||
|
||
classifiers = "License :: OSI Approved :: Apache Software License, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10, Programming Language :: Python :: 3.11, Programming Language :: Python :: 3.12" | ||
|
||
|
||
dependencies = "docker~=7.1.0, setuptools~=68.2.0, PyYAML~=6.0.1, wheel~=0.42.0, twine~=4.0.2, coverage==7.4.4" | ||
|
||
[project.scripts] | ||
easier-docker = "easierdocker.__main__:main" |
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 |
---|---|---|
|
@@ -4,4 +4,5 @@ PyYAML~=6.0.1 | |
wheel~=0.42.0 | ||
twine~=4.0.2 | ||
coverage==7.4.4 | ||
build | ||
|
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 |
---|---|---|
@@ -1,98 +1,102 @@ | ||
import os | ||
|
||
import unittest | ||
from unittest.mock import patch, MagicMock | ||
|
||
from docker.errors import ImageNotFound, APIError, NotFound, DockerException | ||
from easierdocker import EasierDocker | ||
from easierdocker.exceptions import DockerConnectionError, NotFoundImageInDockerHub | ||
|
||
|
||
class TestEasierDocker(unittest.TestCase): | ||
@patch('docker.from_env') | ||
def test_init(self, mock_from_env): | ||
parent_dir = os.path.dirname(os.getcwd()) | ||
host_script = os.path.join(parent_dir, 'example') | ||
container_script = '/path/to/container' | ||
container_config = { | ||
'image': 'python:3.9', | ||
'name': 'python_test', | ||
'volumes': { | ||
f'{host_script}': {'bind': container_script, 'mode': 'rw'} | ||
}, | ||
'detach': True, | ||
'command': ["sh", "-c", f'cd {container_script} &&' | ||
'python docker_example.py'], | ||
def setUp(self): | ||
self.container_config = { | ||
"image": "test_image", | ||
"name": "test_container", | ||
"detach": True | ||
} | ||
self.network_config = {"name": "test_network"} | ||
self.easier_docker = EasierDocker(self.container_config, self.network_config) | ||
|
||
network_config = { | ||
'name': 'bridge', | ||
'driver': 'bridge', | ||
} | ||
@patch("docker.from_env") | ||
def test_docker_connection_error(self, mock_from_env): | ||
mock_from_env.side_effect = DockerException("Docker connection failed") | ||
with self.assertRaises(DockerConnectionError): | ||
EasierDocker(container_config={}, network_config={}) | ||
|
||
mock_client = MagicMock() | ||
mock_from_env.return_value = mock_client | ||
easier_docker = EasierDocker(container_config=container_config, network_config={}) | ||
|
||
self.assertEqual(easier_docker._container_config, container_config) | ||
self.assertEqual(easier_docker._network_config, {}) | ||
|
||
mock_from_env.assert_called_once() | ||
self.assertEqual(easier_docker._client, mock_client) | ||
|
||
easier_docker = EasierDocker(container_config=container_config, network_config=network_config) | ||
self.assertEqual(easier_docker._container_config, container_config) | ||
self.assertEqual(easier_docker._network_config, network_config) | ||
|
||
@patch('docker.from_env') | ||
def test_properties(self, mock_from_env): | ||
parent_dir = os.path.dirname(os.getcwd()) | ||
host_script = os.path.join(parent_dir, 'example') | ||
container_script = '/path/to/container' | ||
container_config = { | ||
'image': 'python:3.9', | ||
'name': 'python_test', | ||
'volumes': { | ||
f'{host_script}': {'bind': container_script, 'mode': 'rw'} | ||
}, | ||
'detach': True, | ||
'command': ["sh", "-c", f'cd {container_script} &&' | ||
'python docker_example.py'], | ||
} | ||
@patch("docker.from_env") | ||
def test_init_success(self, mock_from_env): | ||
client_mock = MagicMock() | ||
mock_from_env.return_value = client_mock | ||
docker_instance = EasierDocker(self.container_config) | ||
self.assertIsNotNone(docker_instance.client) | ||
|
||
network_config = { | ||
'name': 'bridge', | ||
'driver': 'bridge', | ||
} | ||
mock_client = MagicMock() | ||
mock_from_env.return_value = mock_client | ||
easier_docker = EasierDocker(container_config=container_config, network_config=network_config) | ||
self.assertEqual(easier_docker.container_config, container_config) | ||
self.assertEqual(easier_docker.network_config, network_config) | ||
self.assertEqual(easier_docker.client, mock_client) | ||
self.assertEqual(easier_docker.image_name, container_config['image']) | ||
self.assertEqual(easier_docker.container_name, container_config['name']) | ||
|
||
@patch('docker.from_env') | ||
def test_get_images(self, mock_from_env): | ||
parent_dir = os.path.dirname(os.getcwd()) | ||
host_script = os.path.join(parent_dir, 'example') | ||
container_script = '/path/to/container' | ||
container_config = { | ||
'image': 'python:3.9', | ||
'name': 'python_test', | ||
'volumes': { | ||
f'{host_script}': {'bind': container_script, 'mode': 'rw'} | ||
}, | ||
'detach': True, | ||
'command': ["sh", "-c", f'cd {container_script} &&' | ||
'python docker_example.py'], | ||
} | ||
@patch("docker.from_env", side_effect=DockerConnectionError("Docker connection failed")) | ||
def test_init_failure(self, mock_from_env): | ||
with self.assertRaises(DockerConnectionError): | ||
EasierDocker(self.container_config) | ||
|
||
network_config = { | ||
'name': 'bridge', | ||
'driver': 'bridge', | ||
} | ||
mock_client = MagicMock() | ||
mock_from_env.return_value = mock_client | ||
easier_docker = EasierDocker(container_config=container_config, network_config=network_config) | ||
easier_docker._EasierDocker__get_image() | ||
mock_client.images.get.assert_called_once_with(easier_docker.image_name) | ||
@patch("docker.models.images.ImageCollection.get") | ||
def test_get_image_found_locally(self, mock_image_get): | ||
self.easier_docker._EasierDocker__get_image() | ||
mock_image_get.assert_called_once_with("test_image") | ||
|
||
@patch("docker.models.images.ImageCollection.get", side_effect=ImageNotFound("Image not found")) | ||
@patch("docker.api.APIClient.pull", return_value=[ | ||
'{"status": "Pulling", "progress": "50%"}'.encode("utf-8"), | ||
'{"status": "Complete"}'.encode("utf-8") | ||
]) | ||
def test_get_image_pull_success(self, mock_pull, mock_image_get): | ||
self.easier_docker._EasierDocker__get_image() | ||
mock_pull.assert_called_once_with("test_image", stream=True) | ||
|
||
@patch("docker.models.images.ImageCollection.get", side_effect=ImageNotFound("Image not found")) | ||
@patch("docker.api.APIClient.pull", side_effect=NotFound("Image not in Docker Hub")) | ||
def test_get_image_pull_failure(self, mock_pull, mock_image_get): | ||
with self.assertRaises(NotFoundImageInDockerHub): | ||
self.easier_docker._EasierDocker__get_image() | ||
|
||
@patch("docker.models.containers.ContainerCollection.list", return_value=[]) | ||
@patch("docker.models.containers.ContainerCollection.run") | ||
def test_run_container_success(self, mock_run, mock_list): | ||
container_mock = MagicMock() | ||
container_mock.attrs = {"NetworkSettings": {"IPAddress": "127.0.0.1"}, "Created": "now"} | ||
container_mock.name = "test_container" | ||
container_mock.short_id = "12345" | ||
mock_run.return_value = container_mock | ||
|
||
self.easier_docker._EasierDocker__run_container() | ||
mock_run.assert_called_once_with(**self.container_config) | ||
|
||
@patch("docker.models.containers.ContainerCollection.list", return_value=[]) | ||
@patch("docker.models.containers.ContainerCollection.run", side_effect=APIError("API Error")) | ||
def test_run_container_failure(self, mock_run, mock_list): | ||
with self.assertRaises(APIError): | ||
self.easier_docker._EasierDocker__run_container() | ||
|
||
@patch("docker.models.containers.ContainerCollection.list", return_value=[MagicMock()]) | ||
def test_get_container_found(self, mock_list): | ||
container_mock = mock_list.return_value[0] | ||
container_mock.name = "test_container" | ||
container_mock.attrs = {"NetworkSettings": {"IPAddress": "127.0.0.1"}, "Created": "now"} | ||
container_mock.start = MagicMock() | ||
|
||
container = self.easier_docker._EasierDocker__get_container() | ||
self.assertEqual(container_mock, container) | ||
container_mock.start.assert_called_once() | ||
|
||
@patch("docker.models.containers.ContainerCollection.list", return_value=[]) | ||
def test_get_container_not_found(self, mock_list): | ||
container = self.easier_docker._EasierDocker__get_container() | ||
self.assertIsNone(container) | ||
|
||
@patch("docker.models.networks.NetworkCollection.list", return_value=[]) | ||
@patch("docker.models.networks.NetworkCollection.create") | ||
def test_create_network(self, mock_create, mock_list): | ||
self.easier_docker._EasierDocker__create_network() | ||
mock_create.assert_called_once_with(**self.network_config) | ||
|
||
@patch("docker.client.DockerClient.networks", new_callable=MagicMock) | ||
def test_create_network_exists(self, mock_networks): | ||
mock_network = MagicMock(name="test_network", short_id="short_id_1") | ||
mock_networks.list.return_value = [mock_network] | ||
self.easier_docker._EasierDocker__create_network() | ||
mock_networks.list.assert_called_once() |