Merge "Add "run" parameter to the exec API"

This commit is contained in:
Jenkins 2017-03-29 14:23:20 +00:00 committed by Gerrit Code Review
commit 15c540d3dc
8 changed files with 48 additions and 23 deletions

View File

@ -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

View File

@ -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))

View File

@ -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,

View File

@ -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']}

View File

@ -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):

View File

@ -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()

View File

@ -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):

View File

@ -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()