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')
@exception.wrap_pecan_controller_exception
def execute(self, container_id, **kw):
def execute(self, container_id, run=True, **kw):
container = _get_container(container_id)
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')
LOG.debug('Calling compute.container_exec with %s command %s'
% (container.uuid, kw['command']))
context = pecan.request.context
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')
@exception.wrap_pecan_controller_exception

View File

@ -330,11 +330,15 @@ class Manager(object):
raise
@translate_exception
def container_exec(self, context, container, command):
def container_exec(self, context, container, command, run):
# TODO(hongbin): support exec command interactively
LOG.debug('Executing command in container: %s', container.uuid)
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:
LOG.error("Error occurred while calling Docker exec API: %s",
six.text_type(e))

View File

@ -75,9 +75,9 @@ class API(rpc_service.API):
stdout=stdout, stderr=stderr,
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',
container=container, command=command)
container=container, command=command, run=run)
def container_kill(self, context, container, signal):
self._cast(container.host, 'container_kill', container=container,

View File

@ -287,11 +287,15 @@ class DockerDriver(driver.ContainerDriver):
timestamps, tail, since)
@check_container_id
def execute(self, container, command):
def execute_create(self, container, command):
with docker_utils.docker_client() as docker:
create_res = docker.exec_create(
container.container_id, command, True, True, False)
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)
inspect_res = docker.exec_inspect(exec_id)
return {"output": output, "exit_code": inspect_res['ExitCode']}

View File

@ -100,8 +100,12 @@ class ContainerDriver(object):
"""Show logs of a container."""
raise NotImplementedError()
def execute(self, container, command):
"""Execute a command in a running container."""
def execute_create(self, container, command):
"""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()
def kill(self, container, signal):

View File

@ -807,7 +807,7 @@ class TestContainerController(api_base.FunctionalTest):
response = self.app.post(url, cmd)
self.assertEqual(200, response.status_int)
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):
uuid = uuidutils.generate_uuid()

View File

@ -390,20 +390,23 @@ class TestManager(base.TestCase):
self.context, container, True, True,
False, 'all', None)
@mock.patch.object(fake_driver, 'execute')
def test_container_execute(self, mock_execute):
@mock.patch.object(fake_driver, 'execute_run')
@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())
self.compute_manager.container_exec(
self.context, container, 'fake_cmd')
mock_execute.assert_called_once_with(container, 'fake_cmd')
self.context, container, 'fake_cmd', True)
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')
def test_container_execute_failed(self, mock_execute):
@mock.patch.object(fake_driver, 'execute_create')
def test_container_execute_failed(self, mock_execute_create):
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.compute_manager.container_exec,
self.context, container, 'fake_cmd')
self.context, container, 'fake_cmd', True)
@mock.patch.object(fake_driver, '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,
'all', None)
def test_execute(self):
def test_execute_create(self):
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_inspect = mock.Mock(
return_value={u'ExitCode': 0})
mock_container = mock.MagicMock()
self.driver.execute(mock_container, 'ls')
self.mock_docker.exec_create.assert_called_once_with(
mock_container.container_id, 'ls', True, True, False)
self.driver.execute_run('test')
self.mock_docker.exec_start.assert_called_once_with('test', False,
False, False)
self.mock_docker.exec_inspect.assert_called_once()