mirror of https://github.com/docker/docker-py.git
commit
afcbeb5e4f
|
@ -15,6 +15,7 @@ from .exec_api import ExecApiMixin
|
||||||
from .image import ImageApiMixin
|
from .image import ImageApiMixin
|
||||||
from .network import NetworkApiMixin
|
from .network import NetworkApiMixin
|
||||||
from .plugin import PluginApiMixin
|
from .plugin import PluginApiMixin
|
||||||
|
from .secret import SecretApiMixin
|
||||||
from .service import ServiceApiMixin
|
from .service import ServiceApiMixin
|
||||||
from .swarm import SwarmApiMixin
|
from .swarm import SwarmApiMixin
|
||||||
from .volume import VolumeApiMixin
|
from .volume import VolumeApiMixin
|
||||||
|
@ -48,6 +49,7 @@ class APIClient(
|
||||||
ImageApiMixin,
|
ImageApiMixin,
|
||||||
NetworkApiMixin,
|
NetworkApiMixin,
|
||||||
PluginApiMixin,
|
PluginApiMixin,
|
||||||
|
SecretApiMixin,
|
||||||
ServiceApiMixin,
|
ServiceApiMixin,
|
||||||
SwarmApiMixin,
|
SwarmApiMixin,
|
||||||
VolumeApiMixin):
|
VolumeApiMixin):
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
|
class SecretApiMixin(object):
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
def create_secret(self, name, data, labels=None):
|
||||||
|
"""
|
||||||
|
Create a secret
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (string): Name of the secret
|
||||||
|
data (bytes): Secret data to be stored
|
||||||
|
labels (dict): A mapping of labels to assign to the secret
|
||||||
|
|
||||||
|
Returns (dict): ID of the newly created secret
|
||||||
|
"""
|
||||||
|
if not isinstance(data, bytes):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
data = base64.b64encode(data)
|
||||||
|
if six.PY3:
|
||||||
|
data = data.decode('ascii')
|
||||||
|
body = {
|
||||||
|
'Data': data,
|
||||||
|
'Name': name,
|
||||||
|
'Labels': labels
|
||||||
|
}
|
||||||
|
|
||||||
|
url = self._url('/secrets/create')
|
||||||
|
return self._result(
|
||||||
|
self._post_json(url, data=body), True
|
||||||
|
)
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
@utils.check_resource
|
||||||
|
def inspect_secret(self, id):
|
||||||
|
"""
|
||||||
|
Retrieve secret metadata
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (string): Full ID of the secret to remove
|
||||||
|
|
||||||
|
Returns (dict): A dictionary of metadata
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
if no secret with that ID exists
|
||||||
|
"""
|
||||||
|
url = self._url('/secrets/{0}', id)
|
||||||
|
return self._result(self._get(url), True)
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
@utils.check_resource
|
||||||
|
def remove_secret(self, id):
|
||||||
|
"""
|
||||||
|
Remove a secret
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (string): Full ID of the secret to remove
|
||||||
|
|
||||||
|
Returns (boolean): True if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
if no secret with that ID exists
|
||||||
|
"""
|
||||||
|
url = self._url('/secrets/{0}', id)
|
||||||
|
res = self._delete(url)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
def secrets(self, filters=None):
|
||||||
|
"""
|
||||||
|
List secrets
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filters (dict): A map of filters to process on the secrets
|
||||||
|
list. Available filters: ``names``
|
||||||
|
|
||||||
|
Returns (list): A list of secrets
|
||||||
|
"""
|
||||||
|
url = self._url('/secrets')
|
||||||
|
params = {}
|
||||||
|
if filters:
|
||||||
|
params['filters'] = utils.convert_filters(filters)
|
||||||
|
return self._result(self._get(url, params=params), True)
|
|
@ -4,6 +4,7 @@ from .models.images import ImageCollection
|
||||||
from .models.networks import NetworkCollection
|
from .models.networks import NetworkCollection
|
||||||
from .models.nodes import NodeCollection
|
from .models.nodes import NodeCollection
|
||||||
from .models.plugins import PluginCollection
|
from .models.plugins import PluginCollection
|
||||||
|
from .models.secrets import SecretCollection
|
||||||
from .models.services import ServiceCollection
|
from .models.services import ServiceCollection
|
||||||
from .models.swarm import Swarm
|
from .models.swarm import Swarm
|
||||||
from .models.volumes import VolumeCollection
|
from .models.volumes import VolumeCollection
|
||||||
|
@ -118,6 +119,13 @@ class DockerClient(object):
|
||||||
"""
|
"""
|
||||||
return PluginCollection(client=self)
|
return PluginCollection(client=self)
|
||||||
|
|
||||||
|
def secrets(self):
|
||||||
|
"""
|
||||||
|
An object for managing secrets on the server. See the
|
||||||
|
:doc:`secrets documentation <secrets>` for full details.
|
||||||
|
"""
|
||||||
|
return SecretCollection(client=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def services(self):
|
def services(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
from ..api import APIClient
|
||||||
|
from .resource import Model, Collection
|
||||||
|
|
||||||
|
|
||||||
|
class Secret(Model):
|
||||||
|
"""A secret."""
|
||||||
|
id_attribute = 'ID'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s: '%s'>" % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.attrs['Spec']['Name']
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
"""
|
||||||
|
Remove this secret.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If secret failed to remove.
|
||||||
|
"""
|
||||||
|
return self.client.api.remove_secret(self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class SecretCollection(Collection):
|
||||||
|
"""Secrets on the Docker server."""
|
||||||
|
model = Secret
|
||||||
|
|
||||||
|
def create(self, **kwargs):
|
||||||
|
obj = self.client.api.create_secret(**kwargs)
|
||||||
|
return self.prepare_model(obj)
|
||||||
|
create.__doc__ = APIClient.create_secret.__doc__
|
||||||
|
|
||||||
|
def get(self, secret_id):
|
||||||
|
"""
|
||||||
|
Get a secret.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
secret_id (str): Secret ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(:py:class:`Secret`): The secret.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
If the secret does not exist.
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If the server returns an error.
|
||||||
|
"""
|
||||||
|
return self.prepare_model(self.client.api.inspect_secret(secret_id))
|
||||||
|
|
||||||
|
def list(self, **kwargs):
|
||||||
|
"""
|
||||||
|
List secrets. Similar to the ``docker secret ls`` command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filters (dict): Server-side list filtering options.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list of :py:class:`Secret`): The secrets.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If the server returns an error.
|
||||||
|
"""
|
||||||
|
resp = self.client.api.secrets(**kwargs)
|
||||||
|
return [self.prepare_model(obj) for obj in resp]
|
|
@ -109,6 +109,8 @@ class ServiceCollection(Collection):
|
||||||
the service to. Default: ``None``.
|
the service to. Default: ``None``.
|
||||||
resources (Resources): Resource limits and reservations.
|
resources (Resources): Resource limits and reservations.
|
||||||
restart_policy (RestartPolicy): Restart policy for containers.
|
restart_policy (RestartPolicy): Restart policy for containers.
|
||||||
|
secrets (list of :py:class:`docker.types.SecretReference`): List
|
||||||
|
of secrets accessible to containers for this service.
|
||||||
stop_grace_period (int): Amount of time to wait for
|
stop_grace_period (int): Amount of time to wait for
|
||||||
containers to terminate before forcefully killing them.
|
containers to terminate before forcefully killing them.
|
||||||
update_config (UpdateConfig): Specification for the update strategy
|
update_config (UpdateConfig): Specification for the update strategy
|
||||||
|
@ -179,6 +181,7 @@ CONTAINER_SPEC_KWARGS = [
|
||||||
'labels',
|
'labels',
|
||||||
'mounts',
|
'mounts',
|
||||||
'stop_grace_period',
|
'stop_grace_period',
|
||||||
|
'secrets',
|
||||||
]
|
]
|
||||||
|
|
||||||
# kwargs to copy straight over to TaskTemplate
|
# kwargs to copy straight over to TaskTemplate
|
||||||
|
|
|
@ -4,6 +4,6 @@ from .healthcheck import Healthcheck
|
||||||
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
|
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
|
||||||
from .services import (
|
from .services import (
|
||||||
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
|
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
|
||||||
ServiceMode, TaskTemplate, UpdateConfig
|
SecretReference, ServiceMode, TaskTemplate, UpdateConfig
|
||||||
)
|
)
|
||||||
from .swarm import SwarmSpec, SwarmExternalCA
|
from .swarm import SwarmSpec, SwarmExternalCA
|
||||||
|
|
|
@ -2,7 +2,7 @@ import six
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
from ..constants import IS_WINDOWS_PLATFORM
|
from ..constants import IS_WINDOWS_PLATFORM
|
||||||
from ..utils import format_environment, split_command
|
from ..utils import check_resource, format_environment, split_command
|
||||||
|
|
||||||
|
|
||||||
class TaskTemplate(dict):
|
class TaskTemplate(dict):
|
||||||
|
@ -79,9 +79,12 @@ class ContainerSpec(dict):
|
||||||
:py:class:`~docker.types.Mount` class for details.
|
:py:class:`~docker.types.Mount` class for details.
|
||||||
stop_grace_period (int): Amount of time to wait for the container to
|
stop_grace_period (int): Amount of time to wait for the container to
|
||||||
terminate before forcefully killing it.
|
terminate before forcefully killing it.
|
||||||
|
secrets (list of py:class:`SecretReference`): List of secrets to be
|
||||||
|
made available inside the containers.
|
||||||
"""
|
"""
|
||||||
def __init__(self, image, command=None, args=None, env=None, workdir=None,
|
def __init__(self, image, command=None, args=None, env=None, workdir=None,
|
||||||
user=None, labels=None, mounts=None, stop_grace_period=None):
|
user=None, labels=None, mounts=None, stop_grace_period=None,
|
||||||
|
secrets=None):
|
||||||
self['Image'] = image
|
self['Image'] = image
|
||||||
|
|
||||||
if isinstance(command, six.string_types):
|
if isinstance(command, six.string_types):
|
||||||
|
@ -109,6 +112,11 @@ class ContainerSpec(dict):
|
||||||
if stop_grace_period is not None:
|
if stop_grace_period is not None:
|
||||||
self['StopGracePeriod'] = stop_grace_period
|
self['StopGracePeriod'] = stop_grace_period
|
||||||
|
|
||||||
|
if secrets is not None:
|
||||||
|
if not isinstance(secrets, list):
|
||||||
|
raise TypeError('secrets must be a list')
|
||||||
|
self['Secrets'] = secrets
|
||||||
|
|
||||||
|
|
||||||
class Mount(dict):
|
class Mount(dict):
|
||||||
"""
|
"""
|
||||||
|
@ -410,3 +418,31 @@ class ServiceMode(dict):
|
||||||
if self.mode != 'replicated':
|
if self.mode != 'replicated':
|
||||||
return None
|
return None
|
||||||
return self['replicated'].get('Replicas')
|
return self['replicated'].get('Replicas')
|
||||||
|
|
||||||
|
|
||||||
|
class SecretReference(dict):
|
||||||
|
"""
|
||||||
|
Secret reference to be used as part of a :py:class:`ContainerSpec`.
|
||||||
|
Describes how a secret is made accessible inside the service's
|
||||||
|
containers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
secret_id (string): Secret's ID
|
||||||
|
secret_name (string): Secret's name as defined at its creation.
|
||||||
|
filename (string): Name of the file containing the secret. Defaults
|
||||||
|
to the secret's name if not specified.
|
||||||
|
uid (string): UID of the secret file's owner. Default: 0
|
||||||
|
gid (string): GID of the secret file's group. Default: 0
|
||||||
|
mode (int): File access mode inside the container. Default: 0o444
|
||||||
|
"""
|
||||||
|
@check_resource
|
||||||
|
def __init__(self, secret_id, secret_name, filename=None, uid=None,
|
||||||
|
gid=None, mode=0o444):
|
||||||
|
self['SecretName'] = secret_name
|
||||||
|
self['SecretID'] = secret_id
|
||||||
|
self['File'] = {
|
||||||
|
'Name': filename or secret_name,
|
||||||
|
'UID': uid or '0',
|
||||||
|
'GID': gid or '0',
|
||||||
|
'Mode': mode
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ def check_resource(f):
|
||||||
resource_id = resource_id.get('Id', resource_id.get('ID'))
|
resource_id = resource_id.get('Id', resource_id.get('ID'))
|
||||||
if not resource_id:
|
if not resource_id:
|
||||||
raise errors.NullResource(
|
raise errors.NullResource(
|
||||||
'image or container param is undefined'
|
'Resource ID was not provided'
|
||||||
)
|
)
|
||||||
return f(self, resource_id, *args, **kwargs)
|
return f(self, resource_id, *args, **kwargs)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import docker
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..helpers import force_leave_swarm, requires_api_version
|
||||||
|
from .base import BaseAPIIntegrationTest
|
||||||
|
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
class SecretAPITest(BaseAPIIntegrationTest):
|
||||||
|
def setUp(self):
|
||||||
|
super(SecretAPITest, self).setUp()
|
||||||
|
self.init_swarm()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(SecretAPITest, self).tearDown()
|
||||||
|
force_leave_swarm(self.client)
|
||||||
|
|
||||||
|
def test_create_secret(self):
|
||||||
|
secret_id = self.client.create_secret(
|
||||||
|
'favorite_character', 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
assert 'ID' in secret_id
|
||||||
|
data = self.client.inspect_secret(secret_id)
|
||||||
|
assert data['Spec']['Name'] == 'favorite_character'
|
||||||
|
|
||||||
|
def test_create_secret_unicode_data(self):
|
||||||
|
secret_id = self.client.create_secret(
|
||||||
|
'favorite_character', u'いざよいさくや'
|
||||||
|
)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
assert 'ID' in secret_id
|
||||||
|
data = self.client.inspect_secret(secret_id)
|
||||||
|
assert data['Spec']['Name'] == 'favorite_character'
|
||||||
|
|
||||||
|
def test_inspect_secret(self):
|
||||||
|
secret_name = 'favorite_character'
|
||||||
|
secret_id = self.client.create_secret(
|
||||||
|
secret_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
data = self.client.inspect_secret(secret_id)
|
||||||
|
assert data['Spec']['Name'] == secret_name
|
||||||
|
assert 'ID' in data
|
||||||
|
assert 'Version' in data
|
||||||
|
|
||||||
|
def test_remove_secret(self):
|
||||||
|
secret_name = 'favorite_character'
|
||||||
|
secret_id = self.client.create_secret(
|
||||||
|
secret_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
|
||||||
|
assert self.client.remove_secret(secret_id)
|
||||||
|
with pytest.raises(docker.errors.NotFound):
|
||||||
|
self.client.inspect_secret(secret_id)
|
||||||
|
|
||||||
|
def test_list_secrets(self):
|
||||||
|
secret_name = 'favorite_character'
|
||||||
|
secret_id = self.client.create_secret(
|
||||||
|
secret_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
|
||||||
|
data = self.client.secrets(filters={'names': ['favorite_character']})
|
||||||
|
assert len(data) == 1
|
||||||
|
assert data[0]['ID'] == secret_id['ID']
|
|
@ -1,4 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
|
|
||||||
|
@ -24,6 +27,21 @@ class ServiceTest(BaseAPIIntegrationTest):
|
||||||
def get_service_name(self):
|
def get_service_name(self):
|
||||||
return 'dockerpytest_{0:x}'.format(random.getrandbits(64))
|
return 'dockerpytest_{0:x}'.format(random.getrandbits(64))
|
||||||
|
|
||||||
|
def get_service_container(self, service_name, attempts=20, interval=0.5):
|
||||||
|
# There is some delay between the service's creation and the creation
|
||||||
|
# of the service's containers. This method deals with the uncertainty
|
||||||
|
# when trying to retrieve the container associated with a service.
|
||||||
|
while True:
|
||||||
|
containers = self.client.containers(
|
||||||
|
filters={'name': [service_name]}, quiet=True
|
||||||
|
)
|
||||||
|
if len(containers) > 0:
|
||||||
|
return containers[0]
|
||||||
|
attempts -= 1
|
||||||
|
if attempts <= 0:
|
||||||
|
return None
|
||||||
|
time.sleep(interval)
|
||||||
|
|
||||||
def create_simple_service(self, name=None):
|
def create_simple_service(self, name=None):
|
||||||
if name:
|
if name:
|
||||||
name = 'dockerpytest_{0}'.format(name)
|
name = 'dockerpytest_{0}'.format(name)
|
||||||
|
@ -317,3 +335,55 @@ class ServiceTest(BaseAPIIntegrationTest):
|
||||||
new_index = svc_info['Version']['Index']
|
new_index = svc_info['Version']['Index']
|
||||||
assert new_index > version_index
|
assert new_index > version_index
|
||||||
assert svc_info['Spec']['TaskTemplate']['ForceUpdate'] == 10
|
assert svc_info['Spec']['TaskTemplate']['ForceUpdate'] == 10
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_secret(self):
|
||||||
|
secret_name = 'favorite_touhou'
|
||||||
|
secret_data = b'phantasmagoria of flower view'
|
||||||
|
secret_id = self.client.create_secret(secret_name, secret_data)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
secret_ref = docker.types.SecretReference(secret_id, secret_name)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['top'], secrets=[secret_ref]
|
||||||
|
)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(container_spec)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'Secrets' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
secrets = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Secrets']
|
||||||
|
assert secrets[0] == secret_ref
|
||||||
|
|
||||||
|
container = self.get_service_container(name)
|
||||||
|
assert container is not None
|
||||||
|
exec_id = self.client.exec_create(
|
||||||
|
container, 'cat /run/secrets/{0}'.format(secret_name)
|
||||||
|
)
|
||||||
|
assert self.client.exec_start(exec_id) == secret_data
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_unicode_secret(self):
|
||||||
|
secret_name = 'favorite_touhou'
|
||||||
|
secret_data = u'東方花映塚'
|
||||||
|
secret_id = self.client.create_secret(secret_name, secret_data)
|
||||||
|
self.tmp_secrets.append(secret_id)
|
||||||
|
secret_ref = docker.types.SecretReference(secret_id, secret_name)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['top'], secrets=[secret_ref]
|
||||||
|
)
|
||||||
|
task_tmpl = docker.types.TaskTemplate(container_spec)
|
||||||
|
name = self.get_service_name()
|
||||||
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
|
assert 'Secrets' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
secrets = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Secrets']
|
||||||
|
assert secrets[0] == secret_ref
|
||||||
|
|
||||||
|
container = self.get_service_container(name)
|
||||||
|
assert container is not None
|
||||||
|
exec_id = self.client.exec_create(
|
||||||
|
container, 'cat /run/secrets/{0}'.format(secret_name)
|
||||||
|
)
|
||||||
|
container_secret = self.client.exec_start(exec_id)
|
||||||
|
container_secret = container_secret.decode('utf-8')
|
||||||
|
assert container_secret == secret_data
|
||||||
|
|
|
@ -28,6 +28,7 @@ class BaseIntegrationTest(unittest.TestCase):
|
||||||
self.tmp_volumes = []
|
self.tmp_volumes = []
|
||||||
self.tmp_networks = []
|
self.tmp_networks = []
|
||||||
self.tmp_plugins = []
|
self.tmp_plugins = []
|
||||||
|
self.tmp_secrets = []
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
client = docker.from_env(version=TEST_API_VERSION)
|
client = docker.from_env(version=TEST_API_VERSION)
|
||||||
|
@ -52,6 +53,12 @@ class BaseIntegrationTest(unittest.TestCase):
|
||||||
except docker.errors.APIError:
|
except docker.errors.APIError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
for secret in self.tmp_secrets:
|
||||||
|
try:
|
||||||
|
client.api.remove_secret(secret)
|
||||||
|
except docker.errors.APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
for folder in self.tmp_folders:
|
for folder in self.tmp_folders:
|
||||||
shutil.rmtree(folder)
|
shutil.rmtree(folder)
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class StartContainerTest(BaseAPIClientTest):
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(excinfo.value),
|
str(excinfo.value),
|
||||||
'image or container param is undefined',
|
'Resource ID was not provided',
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
@ -53,7 +53,7 @@ class StartContainerTest(BaseAPIClientTest):
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(excinfo.value),
|
str(excinfo.value),
|
||||||
'image or container param is undefined',
|
'Resource ID was not provided',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_start_container_regression_573(self):
|
def test_start_container_regression_573(self):
|
||||||
|
@ -1559,7 +1559,7 @@ class ContainerTest(BaseAPIClientTest):
|
||||||
self.client.inspect_container(arg)
|
self.client.inspect_container(arg)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
excinfo.value.args[0], 'image or container param is undefined'
|
excinfo.value.args[0], 'Resource ID was not provided'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_container_stats(self):
|
def test_container_stats(self):
|
||||||
|
|
|
@ -204,7 +204,7 @@ class ImageTest(BaseAPIClientTest):
|
||||||
self.client.inspect_image(arg)
|
self.client.inspect_image(arg)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
excinfo.value.args[0], 'image or container param is undefined'
|
excinfo.value.args[0], 'Resource ID was not provided'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_insert_image(self):
|
def test_insert_image(self):
|
||||||
|
|
Loading…
Reference in New Issue