Docker cannot start a new instance because of an internal error
The root cause for the issue is that the Docker rest API does not longer deliver the container list for v1.4 api calls. The Docker API must be upgraded to 1.7. Docker API 1.7 is a good choice since that is the first docker version which officially supports rhel/centos. In addition will docker client only parse json if the response needed (to_json()) and this method will not catch json parse exception which makes finding th eroot cause of a problem in the future easier. Change-Id: I53dfac5f09ee021c6ed4763f7f164206e8ab32ff Closes-Bug: #1278104
This commit is contained in:
parent
a710452674
commit
423f344c59
|
@ -40,7 +40,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_list_containers(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('GET', '/v1.4/containers/ps?all=1&limit=50',
|
||||
mock_conn.request('GET', '/v1.7/containers/ps?all=1',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200, data='[]',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -180,7 +180,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_start_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/start',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/start',
|
||||
body='{}',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200,
|
||||
|
@ -197,7 +197,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_start_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/start',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/start',
|
||||
body='{}',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400)
|
||||
|
@ -213,7 +213,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_inspect_image(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('GET', '/v1.4/images/XXX/json',
|
||||
mock_conn.request('GET', '/v1.7/images/XXX/json',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200, data='{"name": "XXX"}',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -230,7 +230,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_inspect_image_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('GET', '/v1.4/images/XXX/json',
|
||||
mock_conn.request('GET', '/v1.7/images/XXX/json',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(404)
|
||||
mock_conn.getresponse().AndReturn(response)
|
||||
|
@ -246,7 +246,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_inspect_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('GET', '/v1.4/containers/XXX/json',
|
||||
mock_conn.request('GET', '/v1.7/containers/XXX/json',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200, data='{"id": "XXX"}',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -263,7 +263,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_inspect_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('GET', '/v1.4/containers/XXX/json',
|
||||
mock_conn.request('GET', '/v1.7/containers/XXX/json',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(404)
|
||||
mock_conn.getresponse().AndReturn(response)
|
||||
|
@ -279,7 +279,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_stop_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/stop?t=5',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/stop?t=5',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(204,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -295,7 +295,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_kill_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/kill',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/kill',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(204,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -311,7 +311,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_stop_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/stop?t=5',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/stop?t=5',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400)
|
||||
mock_conn.getresponse().AndReturn(response)
|
||||
|
@ -326,7 +326,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_kill_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/containers/XXX/kill',
|
||||
mock_conn.request('POST', '/v1.7/containers/XXX/kill',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400)
|
||||
mock_conn.getresponse().AndReturn(response)
|
||||
|
@ -341,7 +341,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_destroy_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('DELETE', '/v1.4/containers/XXX',
|
||||
mock_conn.request('DELETE', '/v1.7/containers/XXX',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(204,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -357,7 +357,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_destroy_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('DELETE', '/v1.4/containers/XXX',
|
||||
mock_conn.request('DELETE', '/v1.7/containers/XXX',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400)
|
||||
mock_conn.getresponse().AndReturn(response)
|
||||
|
@ -372,7 +372,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_pull_repository(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/images/create?fromImage=ping',
|
||||
mock_conn.request('POST', '/v1.7/images/create?fromImage=ping',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -388,7 +388,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_pull_repository_tag(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
url = '/v1.4/images/create?fromImage=ping&tag=pong'
|
||||
url = '/v1.7/images/create?fromImage=ping&tag=pong'
|
||||
mock_conn.request('POST', url,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200,
|
||||
|
@ -405,7 +405,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_pull_repository_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/images/create?fromImage=ping',
|
||||
mock_conn.request('POST', '/v1.7/images/create?fromImage=ping',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -423,7 +423,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
|
||||
body = ('{"username":"foo","password":"bar",'
|
||||
'"auth":"","email":"foo@bar.bar"}')
|
||||
mock_conn.request('POST', '/v1.4/images/ping/push',
|
||||
mock_conn.request('POST', '/v1.7/images/ping/push',
|
||||
headers={'Content-Type': 'application/json'},
|
||||
body=body)
|
||||
response = FakeResponse(200,
|
||||
|
@ -442,7 +442,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
|
||||
body = ('{"username":"foo","password":"bar",'
|
||||
'"auth":"","email":"foo@bar.bar"}')
|
||||
mock_conn.request('POST', '/v1.4/images/ping/push',
|
||||
mock_conn.request('POST', '/v1.7/images/ping/push',
|
||||
headers={'Content-Type': 'application/json'},
|
||||
body=body)
|
||||
response = FakeResponse(400,
|
||||
|
@ -459,7 +459,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_commit_container(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/commit?container=XXX&repo=ping',
|
||||
mock_conn.request('POST', '/v1.7/commit?container=XXX&repo=ping',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(201,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -475,7 +475,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_commit_container_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
mock_conn.request('POST', '/v1.4/commit?container=XXX&repo=ping',
|
||||
mock_conn.request('POST', '/v1.7/commit?container=XXX&repo=ping',
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(400,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
@ -491,7 +491,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_get_container_logs(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
url = '/v1.4/containers/XXX/attach?logs=1&stream=0&stdout=1&stderr=1'
|
||||
url = '/v1.7/containers/XXX/attach?logs=1&stream=0&stdout=1&stderr=1'
|
||||
mock_conn.request('POST', url,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(200, data='ping pong',
|
||||
|
@ -509,7 +509,7 @@ class DockerHTTPClientTestCase(test.NoDBTestCase):
|
|||
def test_get_container_logs_bad_return_code(self):
|
||||
mock_conn = self.mox.CreateMockAnything()
|
||||
|
||||
url = '/v1.4/containers/XXX/attach?logs=1&stream=0&stdout=1&stderr=1'
|
||||
url = '/v1.7/containers/XXX/attach?logs=1&stream=0&stdout=1&stderr=1'
|
||||
mock_conn.request('POST', url,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
response = FakeResponse(404)
|
||||
|
|
|
@ -19,6 +19,7 @@ import socket
|
|||
from eventlet.green import httplib
|
||||
import six
|
||||
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
|
@ -50,23 +51,36 @@ def filter_data(f):
|
|||
|
||||
|
||||
class Response(object):
|
||||
def __init__(self, http_response, skip_body=False):
|
||||
def __init__(self, http_response, url=None):
|
||||
self.url = url
|
||||
self._response = http_response
|
||||
self.code = int(http_response.status)
|
||||
self.data = http_response.read()
|
||||
self.json = self._decode_json(self.data)
|
||||
self._json = None
|
||||
|
||||
def read(self, size=None):
|
||||
return self._response.read(size)
|
||||
|
||||
@filter_data
|
||||
def _decode_json(self, data):
|
||||
def to_json(self, default=None):
|
||||
if not self._json:
|
||||
self._json = self._decode_json(self.data, default)
|
||||
return self._json
|
||||
|
||||
def _validate_content_type(self):
|
||||
# Docker does not return always the correct Content-Type.
|
||||
# Lets try to parse the response anyway since json is requested.
|
||||
if self._response.getheader('Content-Type') != 'application/json':
|
||||
return
|
||||
try:
|
||||
return jsonutils.loads(self.data)
|
||||
except ValueError:
|
||||
return
|
||||
LOG.debug(_("Content-Type of response is not application/json"
|
||||
" (Docker bug?). Requested URL %s") % self.url)
|
||||
|
||||
@filter_data
|
||||
def _decode_json(self, data, default=None):
|
||||
if not data:
|
||||
return default
|
||||
self._validate_content_type()
|
||||
# Do not catch ValueError or SyntaxError since that
|
||||
# just hides the root cause of errors.
|
||||
return jsonutils.loads(data)
|
||||
|
||||
|
||||
class UnixHTTPConnection(httplib.HTTPConnection):
|
||||
|
@ -100,13 +114,13 @@ class DockerHTTPClient(object):
|
|||
kwargs['headers'] = headers
|
||||
conn = self.connection
|
||||
conn.request(*args, **kwargs)
|
||||
return Response(conn.getresponse())
|
||||
return Response(conn.getresponse(), url=args[1])
|
||||
|
||||
def list_containers(self, _all=True):
|
||||
resp = self.make_request(
|
||||
'GET',
|
||||
'/v1.4/containers/ps?all={0}&limit=50'.format(int(_all)))
|
||||
return resp.json
|
||||
'/v1.7/containers/ps?all={0}'.format(int(_all)))
|
||||
return resp.to_json(default=[])
|
||||
|
||||
def create_container(self, args, name):
|
||||
data = {
|
||||
|
@ -135,7 +149,7 @@ class DockerHTTPClient(object):
|
|||
body=jsonutils.dumps(data))
|
||||
if resp.code != 201:
|
||||
return
|
||||
obj = jsonutils.loads(resp.data)
|
||||
obj = resp.to_json()
|
||||
for k, v in obj.iteritems():
|
||||
if k.lower() == 'id':
|
||||
return v
|
||||
|
@ -143,48 +157,48 @@ class DockerHTTPClient(object):
|
|||
def start_container(self, container_id):
|
||||
resp = self.make_request(
|
||||
'POST',
|
||||
'/v1.4/containers/{0}/start'.format(container_id),
|
||||
'/v1.7/containers/{0}/start'.format(container_id),
|
||||
body='{}')
|
||||
return (resp.code == 200)
|
||||
|
||||
def inspect_image(self, image_name):
|
||||
resp = self.make_request(
|
||||
'GET',
|
||||
'/v1.4/images/{0}/json'.format(image_name))
|
||||
'/v1.7/images/{0}/json'.format(image_name))
|
||||
if resp.code != 200:
|
||||
return
|
||||
return resp.json
|
||||
return resp.to_json()
|
||||
|
||||
def inspect_container(self, container_id):
|
||||
resp = self.make_request(
|
||||
'GET',
|
||||
'/v1.4/containers/{0}/json'.format(container_id))
|
||||
'/v1.7/containers/{0}/json'.format(container_id))
|
||||
if resp.code != 200:
|
||||
return
|
||||
return resp.json
|
||||
return resp.to_json()
|
||||
|
||||
def stop_container(self, container_id):
|
||||
timeout = 5
|
||||
resp = self.make_request(
|
||||
'POST',
|
||||
'/v1.4/containers/{0}/stop?t={1}'.format(container_id, timeout))
|
||||
'/v1.7/containers/{0}/stop?t={1}'.format(container_id, timeout))
|
||||
return (resp.code == 204)
|
||||
|
||||
def kill_container(self, container_id):
|
||||
resp = self.make_request(
|
||||
'POST',
|
||||
'/v1.4/containers/{0}/kill'.format(container_id))
|
||||
'/v1.7/containers/{0}/kill'.format(container_id))
|
||||
return (resp.code == 204)
|
||||
|
||||
def destroy_container(self, container_id):
|
||||
resp = self.make_request(
|
||||
'DELETE',
|
||||
'/v1.4/containers/{0}'.format(container_id))
|
||||
'/v1.7/containers/{0}'.format(container_id))
|
||||
return (resp.code == 204)
|
||||
|
||||
def pull_repository(self, name):
|
||||
parts = name.rsplit(':', 1)
|
||||
url = '/v1.4/images/create?fromImage={0}'.format(parts[0])
|
||||
url = '/v1.7/images/create?fromImage={0}'.format(parts[0])
|
||||
if len(parts) > 1:
|
||||
url += '&tag={0}'.format(parts[1])
|
||||
resp = self.make_request('POST', url)
|
||||
|
@ -196,7 +210,7 @@ class DockerHTTPClient(object):
|
|||
return (resp.code == 200)
|
||||
|
||||
def push_repository(self, name, headers=None):
|
||||
url = '/v1.4/images/{0}/push'.format(name)
|
||||
url = '/v1.7/images/{0}/push'.format(name)
|
||||
# NOTE(samalba): docker requires the credentials fields even if
|
||||
# they're not needed here.
|
||||
body = ('{"username":"foo","password":"bar",'
|
||||
|
@ -211,7 +225,7 @@ class DockerHTTPClient(object):
|
|||
|
||||
def commit_container(self, container_id, name):
|
||||
parts = name.rsplit(':', 1)
|
||||
url = '/v1.4/commit?container={0}&repo={1}'.format(container_id,
|
||||
url = '/v1.7/commit?container={0}&repo={1}'.format(container_id,
|
||||
parts[0])
|
||||
if len(parts) > 1:
|
||||
url += '&tag={0}'.format(parts[1])
|
||||
|
@ -221,7 +235,7 @@ class DockerHTTPClient(object):
|
|||
def get_container_logs(self, container_id):
|
||||
resp = self.make_request(
|
||||
'POST',
|
||||
('/v1.4/containers/{0}/attach'
|
||||
('/v1.7/containers/{0}/attach'
|
||||
'?logs=1&stream=0&stdout=1&stderr=1').format(container_id))
|
||||
if resp.code != 200:
|
||||
return
|
||||
|
|
Loading…
Reference in New Issue