Support the command "zun get-archive" and "zun put-archive"

Change-Id: I5c4072368874f660aeff651ae6dd77236e3a5724
Partially-Implements: blueprint support-get-put-archive
This commit is contained in:
Feng Shengqin 2017-01-18 00:32:52 +08:00
parent 66140eb802
commit 94fbfcbdf2
9 changed files with 183 additions and 1 deletions

View File

@ -23,6 +23,8 @@
"container:attach": "rule:admin_or_user",
"container:resize": "rule:admin_or_user",
"container:top": "rule:admin_or_user",
"container:get_archive": "rule:admin_or_user",
"container:put_archive": "rule:admin_or_user",
"image:pull": "rule:default",
"image:get_all": "rule:default",

View File

@ -90,7 +90,9 @@ class ContainersController(rest.RestController):
'rename': ['POST'],
'attach': ['GET'],
'resize': ['POST'],
'top': ['GET']
'top': ['GET'],
'get_archive': ['GET'],
'put_archive': ['POST'],
}
@pecan.expose('json')
@ -464,3 +466,29 @@ class ContainersController(rest.RestController):
context = pecan.request.context
compute_api = pecan.request.compute_api
return compute_api.container_top(context, container, ps_args)
@pecan.expose('json')
@exception.wrap_pecan_controller_exception
def get_archive(self, container_id, **kw):
container = _get_container(container_id)
check_policy_on_container(container.as_dict(), "container:get_archive")
utils.validate_container_state(container, 'get_archive')
LOG.debug('Calling compute.container_get_archive with %s path %s'
% (container.uuid, kw['path']))
context = pecan.request.context
compute_api = pecan.request.compute_api
return compute_api.container_get_archive(context,
container, kw['path'])
@pecan.expose('json')
@exception.wrap_pecan_controller_exception
def put_archive(self, container_id, **kw):
container = _get_container(container_id)
check_policy_on_container(container.as_dict(), "container:put_archive")
utils.validate_container_state(container, 'put_archive')
LOG.debug('Calling compute.container_put_archive with %s path %s'
% (container.uuid, kw['path']))
context = pecan.request.context
compute_api = pecan.request.compute_api
compute_api.container_put_archive(context, container,
kw['path'], kw['data'])

View File

@ -49,6 +49,8 @@ VALID_STATES = {
'attach': ['Running'],
'resize': ['Running'],
'top': ['Running'],
'get_archive': ['Running'],
'put_archive': ['Running'],
}

View File

@ -98,6 +98,12 @@ class API(object):
def container_top(self, context, container, *args):
return self.rpcapi.container_top(context, container, *args)
def container_get_archive(self, context, container, *args):
return self.rpcapi.container_get_archive(context, container, *args)
def container_put_archive(self, context, container, *args):
return self.rpcapi.container_put_archive(context, container, *args)
def image_pull(self, context, image, *args):
return self.rpcapi.image_pull(context, image, *args)

View File

@ -395,6 +395,34 @@ class Manager(object):
LOG.exception(_LE("Unexpected exception: %s"), six.text_type(e))
raise
@translate_exception
def container_get_archive(self, context, container, path):
LOG.debug('Copy resource from the container: %s', container.uuid)
try:
return self.driver.get_archive(container, path)
except exception.DockerError as e:
LOG.error(_LE(
"Error occurred while calling Docker get_archive API: %s"),
six.text_type(e))
raise
except Exception as e:
LOG.exception(_LE("Unexpected exception: %s"), six.text_type(e))
raise
@translate_exception
def container_put_archive(self, context, container, path, data):
LOG.debug('Copy resource to the container: %s', container.uuid)
try:
return self.driver.put_archive(container, path, data)
except exception.DockerError as e:
LOG.error(_LE(
"Error occurred while calling Docker put_archive API: %s"),
six.text_type(e))
raise
except Exception as e:
LOG.exception(_LE("Unexpected exception: %s"), six.text_type(e))
raise
def image_pull(self, context, image):
utils.spawn_n(self._do_image_pull, context, image)

View File

@ -97,6 +97,14 @@ class API(rpc_service.API):
return self._call(container.host, 'container_top',
container=container, ps_args=ps_args)
def container_get_archive(self, context, container, path):
return self._call(container.host, 'container_get_archive',
container=container, path=path)
def container_put_archive(self, context, container, path, data):
return self._call(container.host, 'container_put_archive',
container=container, path=path, data=data)
def image_pull(self, context, image):
# NOTE(hongbin): Image API doesn't support multiple compute nodes
# scenario yet, so we temporarily set host to None and rpc will

View File

@ -350,6 +350,24 @@ class DockerDriver(driver.ContainerDriver):
except errors.APIError:
raise
@check_container_id
def get_archive(self, container, path):
with docker_utils.docker_client() as docker:
try:
return docker.get_archive(container.container_id, path)
except errors.APIError:
raise
@check_container_id
def put_archive(self, container, path, data):
with docker_utils.docker_client() as docker:
try:
f = open(data, 'rb')
filedata = f.read()
docker.put_archive(container.container_id, path, filedata)
except errors.APIError:
raise
def _encode_utf8(self, value):
if six.PY2 and not isinstance(value, unicode):
value = unicode(value)

View File

@ -122,6 +122,14 @@ class ContainerDriver(object):
"""display the running processes inside the container."""
raise NotImplementedError()
def get_archive(self, container, path):
"""copy resource froma container."""
raise NotImplementedError()
def put_archive(self, container, path, data):
"""copy resource to a container."""
raise NotImplementedError()
def create_sandbox(self, context, container, **kwargs):
"""Create a sandbox."""
raise NotImplementedError()

View File

@ -1349,6 +1349,88 @@ class TestContainerController(api_base.FunctionalTest):
container_uuid)
self.assertTrue(mock_container_top.called)
@patch('zun.common.utils.validate_container_state')
@patch('zun.compute.api.API.container_get_archive')
@patch('zun.objects.Container.get_by_uuid')
def test_get_archive_by_uuid(self,
mock_get_by_uuid,
container_get_archive,
mock_validate):
container_get_archive.return_value = ""
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_uuid.return_value = test_container_obj
container_uuid = test_container.get('uuid')
url = '/v1/containers/%s/%s/' % (container_uuid, 'get_archive')
cmd = {'path': '/home/1.txt'}
response = self.app.get(url, cmd)
self.assertEqual(200, response.status_int)
container_get_archive.assert_called_once_with(
mock.ANY, test_container_obj, cmd['path'])
@patch('zun.common.utils.validate_container_state')
@patch('zun.compute.api.API.container_get_archive')
@patch('zun.objects.Container.get_by_name')
def test_get_archive_by_name(self,
mock_get_by_name,
container_get_archive,
mock_validate):
container_get_archive.return_value = ""
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_name.return_value = test_container_obj
container_name = test_container.get('name')
url = '/v1/containers/%s/%s/' % (container_name, 'get_archive')
cmd = {'path': '/home/1.txt'}
response = self.app.get(url, cmd)
self.assertEqual(200, response.status_int)
container_get_archive.assert_called_once_with(
mock.ANY, test_container_obj, cmd['path'])
@patch('zun.common.utils.validate_container_state')
@patch('zun.compute.api.API.container_put_archive')
@patch('zun.objects.Container.get_by_uuid')
def test_put_archive_by_uuid(self,
mock_get_by_uuid,
container_put_archive,
mock_validate):
container_put_archive.return_value = ""
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_uuid.return_value = test_container_obj
container_uuid = test_container.get('uuid')
url = '/v1/containers/%s/%s/' % (container_uuid, 'put_archive')
cmd = {'path': '/home/',
'data': '/home/1.tar'}
response = self.app.post(url, cmd)
self.assertEqual(200, response.status_int)
container_put_archive.assert_called_once_with(
mock.ANY, test_container_obj, cmd['path'], cmd['data'])
@patch('zun.common.utils.validate_container_state')
@patch('zun.compute.api.API.container_put_archive')
@patch('zun.objects.Container.get_by_name')
def test_put_archive_by_name(self,
mock_get_by_name,
container_put_archive,
mock_validate):
container_put_archive.return_value = ""
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_name.return_value = test_container_obj
container_name = test_container.get('name')
url = '/v1/containers/%s/%s/' % (container_name, 'put_archive')
cmd = {'path': '/home/',
'data': '/home/1.tar'}
response = self.app.post(url, cmd)
self.assertEqual(200, response.status_int)
container_put_archive.assert_called_once_with(
mock.ANY, test_container_obj, cmd['path'], cmd['data'])
class TestContainerEnforcement(api_base.FunctionalTest):