Make Docker client timeout configurable
To support long running Docker operations we need to increase our timeout, but a good value for the timeout will change per deployment. So, add it as a config option so that it can be tweaked as necessary. Change-Id: Ie53fa5086e73111bf9f42dbf596172af8d936cf0 Closes-Bug: #1450559
This commit is contained in:
parent
37b3149b5e
commit
fa191a233a
|
@ -133,7 +133,7 @@
|
|||
#rpc_zmq_bind_address = *
|
||||
|
||||
# MatchMaker driver. (string value)
|
||||
#rpc_zmq_matchmaker = oslo_messaging._drivers.matchmaker.MatchMakerLocalhost
|
||||
#rpc_zmq_matchmaker = local
|
||||
|
||||
# ZeroMQ receiver listening port. (integer value)
|
||||
#rpc_zmq_port = 9501
|
||||
|
@ -383,6 +383,10 @@
|
|||
# api version in your environment. (string value)
|
||||
#docker_remote_api_version = 1.17
|
||||
|
||||
# Default timeout in seconds for docker client operations. (integer
|
||||
# value)
|
||||
#default_timeout = 10
|
||||
|
||||
# If set, ignore any SSL validation issues (boolean value)
|
||||
#api_insecure = false
|
||||
|
||||
|
@ -881,9 +885,10 @@
|
|||
#rabbit_ha_queues = false
|
||||
|
||||
# Number of seconds after which the Rabbit broker is considered down
|
||||
# if heartbeat's keep-alive fails (0 disable the heartbeat). (integer
|
||||
# value)
|
||||
#heartbeat_timeout_threshold = 60
|
||||
# if heartbeat's keep-alive fails (0 disables the heartbeat, >0
|
||||
# enables it. Enabling heartbeats requires kombu>=3.0.7 and
|
||||
# amqp>=1.4.0). EXPERIMENTAL (integer value)
|
||||
#heartbeat_timeout_threshold = 0
|
||||
|
||||
# How often times during the heartbeat_timeout_threshold we check the
|
||||
# heartbeat. (integer value)
|
||||
|
|
|
@ -19,13 +19,16 @@ from oslo_config import cfg
|
|||
from magnum.openstack.common import log as logging
|
||||
|
||||
DEFAULT_DOCKER_REMOTE_API_VERSION = '1.17'
|
||||
DEFAULT_DOCKER_TIMEOUT = 10
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class DockerHTTPClient(client.Client):
|
||||
def __init__(self, url='unix://var/run/docker.sock',
|
||||
ver=DEFAULT_DOCKER_REMOTE_API_VERSION):
|
||||
ver=DEFAULT_DOCKER_REMOTE_API_VERSION,
|
||||
timeout=DEFAULT_DOCKER_TIMEOUT):
|
||||
if (CONF.docker.cert_file or
|
||||
CONF.docker.key_file):
|
||||
client_cert = (CONF.docker.cert_file, CONF.docker.key_file)
|
||||
|
@ -43,7 +46,7 @@ class DockerHTTPClient(client.Client):
|
|||
super(DockerHTTPClient, self).__init__(
|
||||
base_url=url,
|
||||
version=ver,
|
||||
timeout=10,
|
||||
timeout=timeout,
|
||||
tls=ssl_config
|
||||
)
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ docker_opts = [
|
|||
default=docker_client.DEFAULT_DOCKER_REMOTE_API_VERSION,
|
||||
help='Docker remote api version. Override it according to '
|
||||
'specific docker api version in your environment.'),
|
||||
cfg.IntOpt('default_timeout',
|
||||
default=docker_client.DEFAULT_DOCKER_TIMEOUT,
|
||||
help='Default timeout in seconds for docker client '
|
||||
'operations.'),
|
||||
cfg.BoolOpt('api_insecure',
|
||||
default=False,
|
||||
help='If set, ignore any SSL validation issues'),
|
||||
|
@ -70,7 +74,8 @@ class Handler(object):
|
|||
def _docker_for_bay(bay):
|
||||
tcp_url = 'tcp://%s:2376' % bay.api_address
|
||||
return docker_client.DockerHTTPClient(tcp_url,
|
||||
CONF.docker.docker_remote_api_version)
|
||||
CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout)
|
||||
|
||||
@classmethod
|
||||
def _docker_for_container(cls, context, container):
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
# Copyright 2015 Rackspace, inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from docker import client as docker_py_client
|
||||
import mock
|
||||
|
||||
from magnum.conductor.handlers.common import docker_client
|
||||
from magnum.tests import base
|
||||
|
||||
|
||||
class DockerClientTestCase(base.BaseTestCase):
|
||||
def test_docker_client_init(self):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_REMOTE_API_VERSION,
|
||||
client.api_version)
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_TIMEOUT,
|
||||
client.timeout)
|
||||
|
||||
def test_docker_client_init_timeout(self):
|
||||
expected_timeout = 300
|
||||
client = docker_client.DockerHTTPClient(timeout=expected_timeout)
|
||||
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_REMOTE_API_VERSION,
|
||||
client.api_version)
|
||||
self.assertEqual(expected_timeout, client.timeout)
|
||||
|
||||
def test_docker_client_init_url(self):
|
||||
expected_url = 'http://127.0.0.1:2375'
|
||||
client = docker_client.DockerHTTPClient(url=expected_url)
|
||||
|
||||
self.assertEqual(expected_url,
|
||||
client.base_url)
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_REMOTE_API_VERSION,
|
||||
client.api_version)
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_TIMEOUT,
|
||||
client.timeout)
|
||||
|
||||
def test_docker_client_init_version(self):
|
||||
expected_version = '1.16'
|
||||
client = docker_client.DockerHTTPClient(ver=expected_version)
|
||||
|
||||
self.assertEqual(expected_version,
|
||||
client.api_version)
|
||||
self.assertEqual(docker_client.DEFAULT_DOCKER_TIMEOUT,
|
||||
client.timeout)
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, 'inspect_container')
|
||||
@mock.patch.object(docker_py_client.Client, 'containers')
|
||||
def test_list_instances(self, mock_containers, mock_inspect):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
containers = [dict(Id=x) for x in range(0, 3)]
|
||||
inspect_results = [dict(Config=dict(Hostname=x)) for x in range(0, 3)]
|
||||
|
||||
mock_containers.return_value = containers
|
||||
mock_inspect.side_effect = inspect_results
|
||||
|
||||
instances = client.list_instances()
|
||||
|
||||
self.assertEqual([0, 1, 2], instances)
|
||||
mock_containers.assert_called_once_with(all=True)
|
||||
mock_inspect.assert_has_calls([mock.call(x) for x in range(0, 3)])
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, 'inspect_container')
|
||||
@mock.patch.object(docker_py_client.Client, 'containers')
|
||||
def test_list_instances_inspect(self, mock_containers, mock_inspect):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
containers = [dict(Id=x) for x in range(0, 3)]
|
||||
inspect_results = [dict(Config=dict(Hostname=x)) for x in range(0, 3)]
|
||||
|
||||
mock_containers.return_value = containers
|
||||
mock_inspect.side_effect = inspect_results
|
||||
|
||||
instances = client.list_instances(inspect=True)
|
||||
|
||||
self.assertEqual(inspect_results, instances)
|
||||
mock_containers.assert_called_once_with(all=True)
|
||||
mock_inspect.assert_has_calls([mock.call(x) for x in range(0, 3)])
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, '_raise_for_status')
|
||||
@mock.patch.object(docker_py_client.Client, '_post')
|
||||
@mock.patch.object(docker_py_client.Client, '_url')
|
||||
def test_pause(self, mock_url, mock_post, mock_raise_for_status):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
client.pause('someid')
|
||||
|
||||
mock_url.assert_called_once_with('/containers/someid/pause')
|
||||
mock_post.assert_callend_once_with(mock_url.return_value)
|
||||
mock_raise_for_status.assert_called_once_with(
|
||||
mock_post.return_value)
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, '_raise_for_status')
|
||||
@mock.patch.object(docker_py_client.Client, '_post')
|
||||
@mock.patch.object(docker_py_client.Client, '_url')
|
||||
def test_pause_container_dict(self, mock_url, mock_post,
|
||||
mock_raise_for_status):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
client.pause(dict(Id='someid'))
|
||||
|
||||
mock_url.assert_called_once_with('/containers/someid/pause')
|
||||
mock_post.assert_callend_once_with(mock_url.return_value)
|
||||
mock_raise_for_status.assert_called_once_with(
|
||||
mock_post.return_value)
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, '_raise_for_status')
|
||||
@mock.patch.object(docker_py_client.Client, '_post')
|
||||
@mock.patch.object(docker_py_client.Client, '_url')
|
||||
def test_unpause(self, mock_url, mock_post, mock_raise_for_status):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
client.unpause('someid')
|
||||
|
||||
mock_url.assert_called_once_with('/containers/someid/unpause')
|
||||
mock_post.assert_callend_once_with(mock_url.return_value)
|
||||
mock_raise_for_status.assert_called_once_with(
|
||||
mock_post.return_value)
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, '_raise_for_status')
|
||||
@mock.patch.object(docker_py_client.Client, '_post')
|
||||
@mock.patch.object(docker_py_client.Client, '_url')
|
||||
def test_unpause_container_dict(self, mock_url, mock_post,
|
||||
mock_raise_for_status):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
client.unpause(dict(Id='someid'))
|
||||
|
||||
mock_url.assert_called_once_with('/containers/someid/unpause')
|
||||
mock_post.assert_callend_once_with(mock_url.return_value)
|
||||
mock_raise_for_status.assert_called_once_with(
|
||||
mock_post.return_value)
|
||||
|
||||
@mock.patch.object(docker_py_client.Client, 'attach')
|
||||
def test_get_container_logs(self, mock_attach):
|
||||
client = docker_client.DockerHTTPClient()
|
||||
|
||||
client.get_container_logs('someid')
|
||||
|
||||
mock_attach.assert_called_once_with('someid',
|
||||
1, 1, 0, 1)
|
|
@ -39,7 +39,8 @@ class TestDockerConductor(base.BaseTestCase):
|
|||
|
||||
self.assertEqual(mock_docker, actual_docker)
|
||||
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version)
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout)
|
||||
mock_docker_client.DockerHTTPClient.assert_called_once_with(*args)
|
||||
|
||||
@mock.patch.object(docker_conductor, 'docker_client')
|
||||
|
@ -62,7 +63,8 @@ class TestDockerConductor(base.BaseTestCase):
|
|||
|
||||
self.assertEqual(mock_docker, actual_docker)
|
||||
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version)
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout)
|
||||
mock_bay_get_by_uuid.assert_called_once_with(mock.sentinel.context,
|
||||
mock_container.bay_uuid)
|
||||
mock_docker_client.DockerHTTPClient.assert_called_once_with(*args)
|
||||
|
@ -91,7 +93,8 @@ class TestDockerConductor(base.BaseTestCase):
|
|||
|
||||
self.assertEqual(mock_docker, actual_docker)
|
||||
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version)
|
||||
args = ('tcp://1.1.1.1:2376', CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout)
|
||||
mock_container_get_by_uuid.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
mock_container.uuid)
|
||||
|
|
Loading…
Reference in New Issue