Merge "Add "run" parameter to the exec API"
This commit is contained in:
commit
15c540d3dc
@ -410,15 +410,21 @@ class ContainersController(rest.RestController):
|
|||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
@exception.wrap_pecan_controller_exception
|
@exception.wrap_pecan_controller_exception
|
||||||
def execute(self, container_id, **kw):
|
def execute(self, container_id, run=True, **kw):
|
||||||
container = _get_container(container_id)
|
container = _get_container(container_id)
|
||||||
check_policy_on_container(container.as_dict(), "container:execute")
|
check_policy_on_container(container.as_dict(), "container:execute")
|
||||||
|
try:
|
||||||
|
run = strutils.bool_from_string(run, strict=True)
|
||||||
|
except ValueError:
|
||||||
|
msg = _('Valid run values are true, false, 0, 1, yes and no')
|
||||||
|
raise exception.InvalidValue(msg)
|
||||||
utils.validate_container_state(container, 'execute')
|
utils.validate_container_state(container, 'execute')
|
||||||
LOG.debug('Calling compute.container_exec with %s command %s'
|
LOG.debug('Calling compute.container_exec with %s command %s'
|
||||||
% (container.uuid, kw['command']))
|
% (container.uuid, kw['command']))
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
compute_api = pecan.request.compute_api
|
compute_api = pecan.request.compute_api
|
||||||
return compute_api.container_exec(context, container, kw['command'])
|
return compute_api.container_exec(context, container, kw['command'],
|
||||||
|
run)
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
@exception.wrap_pecan_controller_exception
|
@exception.wrap_pecan_controller_exception
|
||||||
|
@ -330,11 +330,15 @@ class Manager(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
@translate_exception
|
@translate_exception
|
||||||
def container_exec(self, context, container, command):
|
def container_exec(self, context, container, command, run):
|
||||||
# TODO(hongbin): support exec command interactively
|
# TODO(hongbin): support exec command interactively
|
||||||
LOG.debug('Executing command in container: %s', container.uuid)
|
LOG.debug('Executing command in container: %s', container.uuid)
|
||||||
try:
|
try:
|
||||||
return self.driver.execute(container, command)
|
exec_id = self.driver.execute_create(container, command)
|
||||||
|
if run:
|
||||||
|
return self.driver.execute_run(exec_id)
|
||||||
|
else:
|
||||||
|
return exec_id
|
||||||
except exception.DockerError as e:
|
except exception.DockerError as e:
|
||||||
LOG.error("Error occurred while calling Docker exec API: %s",
|
LOG.error("Error occurred while calling Docker exec API: %s",
|
||||||
six.text_type(e))
|
six.text_type(e))
|
||||||
|
@ -75,9 +75,9 @@ class API(rpc_service.API):
|
|||||||
stdout=stdout, stderr=stderr,
|
stdout=stdout, stderr=stderr,
|
||||||
timestamps=timestamps, tail=tail, since=since)
|
timestamps=timestamps, tail=tail, since=since)
|
||||||
|
|
||||||
def container_exec(self, context, container, command):
|
def container_exec(self, context, container, command, run):
|
||||||
return self._call(container.host, 'container_exec',
|
return self._call(container.host, 'container_exec',
|
||||||
container=container, command=command)
|
container=container, command=command, run=run)
|
||||||
|
|
||||||
def container_kill(self, context, container, signal):
|
def container_kill(self, context, container, signal):
|
||||||
self._cast(container.host, 'container_kill', container=container,
|
self._cast(container.host, 'container_kill', container=container,
|
||||||
|
@ -287,11 +287,15 @@ class DockerDriver(driver.ContainerDriver):
|
|||||||
timestamps, tail, since)
|
timestamps, tail, since)
|
||||||
|
|
||||||
@check_container_id
|
@check_container_id
|
||||||
def execute(self, container, command):
|
def execute_create(self, container, command):
|
||||||
with docker_utils.docker_client() as docker:
|
with docker_utils.docker_client() as docker:
|
||||||
create_res = docker.exec_create(
|
create_res = docker.exec_create(
|
||||||
container.container_id, command, True, True, False)
|
container.container_id, command, True, True, False)
|
||||||
exec_id = create_res['Id']
|
exec_id = create_res['Id']
|
||||||
|
return exec_id
|
||||||
|
|
||||||
|
def execute_run(self, exec_id):
|
||||||
|
with docker_utils.docker_client() as docker:
|
||||||
output = docker.exec_start(exec_id, False, False, False)
|
output = docker.exec_start(exec_id, False, False, False)
|
||||||
inspect_res = docker.exec_inspect(exec_id)
|
inspect_res = docker.exec_inspect(exec_id)
|
||||||
return {"output": output, "exit_code": inspect_res['ExitCode']}
|
return {"output": output, "exit_code": inspect_res['ExitCode']}
|
||||||
|
@ -100,8 +100,12 @@ class ContainerDriver(object):
|
|||||||
"""Show logs of a container."""
|
"""Show logs of a container."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def execute(self, container, command):
|
def execute_create(self, container, command):
|
||||||
"""Execute a command in a running container."""
|
"""Create an execute instance for running a command."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def execute_run(self, exec_id):
|
||||||
|
"""Run the command specified by an execute instance."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def kill(self, container, signal):
|
def kill(self, container, signal):
|
||||||
|
@ -807,7 +807,7 @@ class TestContainerController(api_base.FunctionalTest):
|
|||||||
response = self.app.post(url, cmd)
|
response = self.app.post(url, cmd)
|
||||||
self.assertEqual(200, response.status_int)
|
self.assertEqual(200, response.status_int)
|
||||||
mock_container_exec.assert_called_once_with(
|
mock_container_exec.assert_called_once_with(
|
||||||
mock.ANY, test_container_obj, cmd['command'])
|
mock.ANY, test_container_obj, cmd['command'], True)
|
||||||
|
|
||||||
def test_exec_command_by_uuid_invalid_state(self):
|
def test_exec_command_by_uuid_invalid_state(self):
|
||||||
uuid = uuidutils.generate_uuid()
|
uuid = uuidutils.generate_uuid()
|
||||||
|
@ -390,20 +390,23 @@ class TestManager(base.TestCase):
|
|||||||
self.context, container, True, True,
|
self.context, container, True, True,
|
||||||
False, 'all', None)
|
False, 'all', None)
|
||||||
|
|
||||||
@mock.patch.object(fake_driver, 'execute')
|
@mock.patch.object(fake_driver, 'execute_run')
|
||||||
def test_container_execute(self, mock_execute):
|
@mock.patch.object(fake_driver, 'execute_create')
|
||||||
|
def test_container_execute(self, mock_execute_create, mock_execute_run):
|
||||||
|
mock_execute_create.return_value = 'fake_exec_id'
|
||||||
container = Container(self.context, **utils.get_test_container())
|
container = Container(self.context, **utils.get_test_container())
|
||||||
self.compute_manager.container_exec(
|
self.compute_manager.container_exec(
|
||||||
self.context, container, 'fake_cmd')
|
self.context, container, 'fake_cmd', True)
|
||||||
mock_execute.assert_called_once_with(container, 'fake_cmd')
|
mock_execute_create.assert_called_once_with(container, 'fake_cmd')
|
||||||
|
mock_execute_run.assert_called_once_with('fake_exec_id')
|
||||||
|
|
||||||
@mock.patch.object(fake_driver, 'execute')
|
@mock.patch.object(fake_driver, 'execute_create')
|
||||||
def test_container_execute_failed(self, mock_execute):
|
def test_container_execute_failed(self, mock_execute_create):
|
||||||
container = Container(self.context, **utils.get_test_container())
|
container = Container(self.context, **utils.get_test_container())
|
||||||
mock_execute.side_effect = exception.DockerError
|
mock_execute_create.side_effect = exception.DockerError
|
||||||
self.assertRaises(exception.DockerError,
|
self.assertRaises(exception.DockerError,
|
||||||
self.compute_manager.container_exec,
|
self.compute_manager.container_exec,
|
||||||
self.context, container, 'fake_cmd')
|
self.context, container, 'fake_cmd', True)
|
||||||
|
|
||||||
@mock.patch.object(fake_driver, 'kill')
|
@mock.patch.object(fake_driver, 'kill')
|
||||||
def test_container_kill(self, mock_kill):
|
def test_container_kill(self, mock_kill):
|
||||||
|
@ -238,15 +238,19 @@ class TestDockerDriver(base.DriverTestCase):
|
|||||||
mock_container.container_id, True, True, False, False,
|
mock_container.container_id, True, True, False, False,
|
||||||
'all', None)
|
'all', None)
|
||||||
|
|
||||||
def test_execute(self):
|
def test_execute_create(self):
|
||||||
self.mock_docker.exec_create = mock.Mock(return_value={'Id': 'test'})
|
self.mock_docker.exec_create = mock.Mock(return_value={'Id': 'test'})
|
||||||
|
mock_container = mock.MagicMock()
|
||||||
|
exec_id = self.driver.execute_create(mock_container, 'ls')
|
||||||
|
self.assertEqual('test', exec_id)
|
||||||
|
self.mock_docker.exec_create.assert_called_once_with(
|
||||||
|
mock_container.container_id, 'ls', True, True, False)
|
||||||
|
|
||||||
|
def test_execute_run(self):
|
||||||
self.mock_docker.exec_start = mock.Mock(return_value='test')
|
self.mock_docker.exec_start = mock.Mock(return_value='test')
|
||||||
self.mock_docker.exec_inspect = mock.Mock(
|
self.mock_docker.exec_inspect = mock.Mock(
|
||||||
return_value={u'ExitCode': 0})
|
return_value={u'ExitCode': 0})
|
||||||
mock_container = mock.MagicMock()
|
self.driver.execute_run('test')
|
||||||
self.driver.execute(mock_container, 'ls')
|
|
||||||
self.mock_docker.exec_create.assert_called_once_with(
|
|
||||||
mock_container.container_id, 'ls', True, True, False)
|
|
||||||
self.mock_docker.exec_start.assert_called_once_with('test', False,
|
self.mock_docker.exec_start.assert_called_once_with('test', False,
|
||||||
False, False)
|
False, False)
|
||||||
self.mock_docker.exec_inspect.assert_called_once()
|
self.mock_docker.exec_inspect.assert_called_once()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user