2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
Interface for a repo2docker container engine
|
|
|
|
"""
|
|
|
|
|
|
|
|
from abc import ABC, abstractmethod
|
2020-02-14 13:37:31 +00:00
|
|
|
from traitlets.config import LoggingConfigurable
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html
|
|
|
|
|
|
|
|
|
|
|
|
class Container(ABC):
|
|
|
|
"""
|
|
|
|
Abstract container returned by repo2docker engines
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def reload(self):
|
|
|
|
"""
|
|
|
|
Refresh container attributes
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
2021-07-07 11:59:43 +00:00
|
|
|
def logs(self, *, stream=False, timestamps=False, since=None):
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
Get the container logs.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
stream : bool
|
2020-02-14 16:20:55 +00:00
|
|
|
If `True` return an iterator over the log lines, otherwise return all logs
|
2021-07-07 11:59:43 +00:00
|
|
|
timestamps : bool
|
|
|
|
If `True` log lines will be prefixed with iso8601 timestamps followed by space
|
2021-07-09 08:24:09 +00:00
|
|
|
since : str
|
|
|
|
A timestamp string
|
|
|
|
Should be in the same format as the timestamp prefix given
|
|
|
|
when `timestamps=True`
|
|
|
|
|
2021-07-07 11:59:43 +00:00
|
|
|
If given, start logs from this point,
|
|
|
|
instead of from container start.
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
2020-02-14 16:20:55 +00:00
|
|
|
str or generator of log strings
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def kill(self, *, signal="KILL"):
|
|
|
|
"""
|
|
|
|
Send a signal to the container
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
signal : str
|
|
|
|
The signal, default `KILL`
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def remove(self):
|
|
|
|
"""
|
|
|
|
Remove the container
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def stop(self, *, timeout=10):
|
|
|
|
"""
|
|
|
|
Stop the container
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
timeout : If the container doesn't gracefully stop after this timeout kill it
|
|
|
|
"""
|
|
|
|
|
2021-02-21 00:19:54 +00:00
|
|
|
@abstractmethod
|
|
|
|
def wait(self):
|
|
|
|
"""
|
|
|
|
Wait for the container to stop
|
|
|
|
"""
|
|
|
|
|
2020-02-11 19:01:18 +00:00
|
|
|
@property
|
|
|
|
@abstractmethod
|
|
|
|
def exitcode(self):
|
|
|
|
"""
|
|
|
|
The container exit code if exited
|
|
|
|
"""
|
|
|
|
|
2020-02-11 18:05:02 +00:00
|
|
|
@property
|
|
|
|
@abstractmethod
|
|
|
|
def status(self):
|
|
|
|
"""
|
|
|
|
The status of the container
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
str : The status of the container.
|
|
|
|
Values include `created` `running` `exited`.
|
|
|
|
|
2020-02-14 22:01:08 +00:00
|
|
|
Full list of statuses:
|
|
|
|
https://github.com/moby/moby/blob/v19.03.5/api/swagger.yaml#L4832
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2020-02-14 13:15:04 +00:00
|
|
|
class Image:
|
|
|
|
"""
|
|
|
|
Information about a container image
|
|
|
|
"""
|
|
|
|
|
2020-02-14 22:00:34 +00:00
|
|
|
def __init__(self, *, tags, config=None):
|
2020-02-14 13:15:04 +00:00
|
|
|
self._tags = tags or []
|
2020-02-14 22:00:34 +00:00
|
|
|
self._config = config
|
2020-02-14 13:15:04 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def tags(self):
|
|
|
|
"""
|
|
|
|
A list of tags associated with an image.
|
|
|
|
|
|
|
|
If locally built images have a localhost prefix this prefix should be removed or the image may not be recognised.
|
|
|
|
If there are no tags [] will be returned.
|
|
|
|
"""
|
|
|
|
return self._tags
|
|
|
|
|
2020-02-14 22:00:34 +00:00
|
|
|
@property
|
|
|
|
def config(self):
|
|
|
|
"""
|
|
|
|
A dictionary of image configuration information
|
|
|
|
|
|
|
|
If this is `None` the information has not been loaded.
|
|
|
|
If not `None` this must include the following fields:
|
|
|
|
- WorkingDir: The default working directory
|
|
|
|
"""
|
|
|
|
return self._config
|
|
|
|
|
2020-02-14 13:15:04 +00:00
|
|
|
def __repr__(self):
|
2020-02-14 22:00:34 +00:00
|
|
|
return "Image(tags={},config={})".format(self.tags, self.config)
|
2020-02-14 13:15:04 +00:00
|
|
|
|
|
|
|
|
2020-02-14 13:37:31 +00:00
|
|
|
class ContainerEngine(LoggingConfigurable):
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
2020-02-14 13:15:04 +00:00
|
|
|
Abstract container engine.
|
2020-02-14 13:37:31 +00:00
|
|
|
|
|
|
|
Inherits from LoggingConfigurable, which means it has a log property.
|
|
|
|
Initialised with a reference to the parent so can also be configured using traitlets.
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
|
2020-02-14 16:20:55 +00:00
|
|
|
string_output = True
|
|
|
|
"""
|
|
|
|
Whether progress events should be strings or an object.
|
|
|
|
|
|
|
|
Originally Docker was the only container engine supported by repo2docker.
|
|
|
|
Some operations including build() and push() would return generators of events in a Docker specific format.
|
|
|
|
This format of events is not easily constructable with other engines so the default is to return strings and raise an exception if an error occurs.
|
|
|
|
If an engine returns docker style events set this variable to False.
|
|
|
|
"""
|
|
|
|
|
2020-02-14 13:37:31 +00:00
|
|
|
def __init__(self, *, parent):
|
|
|
|
"""
|
|
|
|
Initialise the container engine
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
parent: Application
|
|
|
|
Reference to the parent application so that its configuration file can be used in this plugin.
|
|
|
|
"""
|
|
|
|
super().__init__(parent=parent)
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
# Based on https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build
|
|
|
|
|
|
|
|
def build(
|
|
|
|
self,
|
|
|
|
*,
|
|
|
|
buildargs={},
|
|
|
|
cache_from=[],
|
|
|
|
container_limits={},
|
|
|
|
tag="",
|
|
|
|
custom_context=False,
|
|
|
|
dockerfile="",
|
|
|
|
fileobj=None,
|
2020-02-14 16:38:42 +00:00
|
|
|
path="",
|
2021-02-21 00:19:54 +00:00
|
|
|
**kwargs,
|
2020-02-11 18:05:02 +00:00
|
|
|
):
|
|
|
|
"""
|
|
|
|
Build a container
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
buildargs : dict
|
|
|
|
Dictionary of build arguments
|
|
|
|
cache_from : list[str]
|
|
|
|
List of images to chech for caching
|
|
|
|
container_limits : dict
|
|
|
|
Dictionary of resources limits. These keys are supported:
|
|
|
|
- `cpusetcpus`
|
|
|
|
- `cpushares`
|
|
|
|
- `memory`
|
|
|
|
- `memswap`
|
|
|
|
tag : str
|
|
|
|
Tag to add to the image
|
|
|
|
|
|
|
|
custom_context : bool
|
|
|
|
If `True` fileobj is a Tar file object containing the build context
|
|
|
|
dockerfile : str
|
|
|
|
Path to Dockerfile within the build context
|
|
|
|
fileobj : tarfile
|
|
|
|
A tar file-like object containing the build context
|
|
|
|
path : str
|
|
|
|
path to the Dockerfile
|
2020-02-14 16:20:55 +00:00
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
A generator of strings. If an error occurs an exception must be thrown.
|
|
|
|
|
|
|
|
If `string_output=True` this should instead be whatever Docker returns:
|
|
|
|
https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/app.py#L725-L735
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
2020-02-14 13:37:31 +00:00
|
|
|
raise NotImplementedError("build not implemented")
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
def images(self):
|
|
|
|
"""
|
|
|
|
List images
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
2020-02-14 13:15:04 +00:00
|
|
|
list[Image] : List of Image objects.
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
2020-02-14 13:37:31 +00:00
|
|
|
raise NotImplementedError("images not implemented")
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
def inspect_image(self, image):
|
|
|
|
"""
|
|
|
|
Get information about an image
|
|
|
|
|
|
|
|
TODO: This is specific to the engine, can we convert it to a standard format?
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
image : str
|
|
|
|
The image
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
2020-02-14 22:00:34 +00:00
|
|
|
Image object with .config dict.
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
2020-02-14 13:37:31 +00:00
|
|
|
raise NotImplementedError("inspect_image not implemented")
|
2020-02-11 18:05:02 +00:00
|
|
|
|
2020-02-14 16:20:55 +00:00
|
|
|
def push(self, image_spec):
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
|
|
|
Push image to a registry
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
image_spec : str
|
|
|
|
The repository spec to push to
|
2020-02-14 16:20:55 +00:00
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
A generator of strings. If an error occurs an exception must be thrown.
|
|
|
|
|
|
|
|
If `string_output=True` this should instead be whatever Docker returns:
|
|
|
|
https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/app.py#L469-L495
|
2020-02-11 18:05:02 +00:00
|
|
|
"""
|
2020-02-14 13:37:31 +00:00
|
|
|
raise NotImplementedError("push not implemented")
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
# Note this is different from the Docker client which has Client.containers.run
|
|
|
|
def run(
|
2020-02-14 13:40:02 +00:00
|
|
|
self,
|
2020-02-11 18:05:02 +00:00
|
|
|
image_spec,
|
|
|
|
*,
|
|
|
|
command=[],
|
|
|
|
environment=[],
|
|
|
|
ports={},
|
|
|
|
publish_all_ports=False,
|
|
|
|
remove=False,
|
2020-02-14 16:38:42 +00:00
|
|
|
volumes={},
|
2021-02-21 00:19:54 +00:00
|
|
|
**kwargs,
|
2020-02-11 18:05:02 +00:00
|
|
|
):
|
|
|
|
"""
|
|
|
|
Run a container
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
image_spec : str
|
|
|
|
The image to run
|
|
|
|
command : list[str]
|
|
|
|
The command to run
|
|
|
|
environment : list[str]
|
|
|
|
List of environment variables in the form `ENVVAR=value`
|
|
|
|
ports : dict
|
2020-02-14 22:01:08 +00:00
|
|
|
Container port bindings in the form generated by `repo2docker.utils.validate_and_generate_port_mapping`
|
|
|
|
https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/utils.py#L95
|
2020-02-11 18:05:02 +00:00
|
|
|
publish_all_ports : bool
|
|
|
|
If `True` publish all ports to host
|
|
|
|
remove : bool
|
|
|
|
If `True` delete container when it completes
|
|
|
|
volumes : dict
|
2020-02-14 22:01:08 +00:00
|
|
|
Volume bindings in the form `{src : dest}`
|
2020-02-11 18:05:02 +00:00
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Container : the running container
|
|
|
|
|
|
|
|
Raises
|
|
|
|
------
|
|
|
|
NotImplementedError
|
|
|
|
This engine does not support running containers
|
|
|
|
"""
|
|
|
|
raise NotImplementedError("Running containers not supported")
|
|
|
|
|
|
|
|
|
|
|
|
class ContainerEngineException(Exception):
|
|
|
|
"""
|
|
|
|
Base class for exceptions in the container engine
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class BuildError(ContainerEngineException):
|
|
|
|
"""
|
|
|
|
Container build error
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class ImageLoadError(ContainerEngineException):
|
|
|
|
"""
|
|
|
|
Container load/push error
|
|
|
|
"""
|