[docs]
class Container:
"""
A Docker container with nginx, configured to serve a Betty site.
"""
_IMAGE_TAG = "betty-serve"
[docs]
def __init__(
self,
www_directory_path: Path,
docker_directory_path: Path,
nginx_configuration_file_path: Path,
):
self._docker_directory_path = docker_directory_path
self._nginx_configuration_file_path = nginx_configuration_file_path
self._www_directory_path = www_directory_path
self._client = docker.from_env()
self.__container: DockerContainer | None = None
async def __aenter__(self) -> None:
await self.start()
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
await self.stop()
[docs]
async def start(self) -> None:
"""
Start the container.
"""
await asyncio.to_thread(self._start)
def _start(self) -> None:
self._client.images.build(
path=str(self._docker_directory_path), tag=self._IMAGE_TAG
)
self._container.start()
self._container.exec_run(["nginx", "-s", "reload"])
[docs]
async def stop(self) -> None:
"""
Stop the container.
"""
await asyncio.to_thread(self._stop)
def _stop(self) -> None:
if self._container is not None:
self._container.stop()
@property
def _container(self) -> DockerContainer:
if self.__container is None:
self.__container = self._client.containers.create(
self._IMAGE_TAG,
auto_remove=True,
detach=True,
volumes={
self._nginx_configuration_file_path: {
"bind": "/etc/nginx/conf.d/betty.conf",
"mode": "ro",
},
self._www_directory_path: {
"bind": "/var/www/betty",
"mode": "ro",
},
},
)
return self.__container
@property
def ip(self) -> str:
"""
The container's public IP address.
"""
return cast(
str,
self._client.api.inspect_container(self._container.id)["NetworkSettings"][
"Networks"
]["bridge"]["IPAddress"],
)