# Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

from unittest import mock
from unittest.mock import patch

from neutronclient.common import exceptions as n_exc
from oslo_utils import uuidutils
from webtest.app import AppError

from zun.common import exception
from zun import objects
from zun.tests.unit.api import base as api_base
from zun.tests.unit.db import utils
from zun.tests.unit.objects import utils as obj_utils


class TestContainerController(api_base.FunctionalTest):
    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_run_container(self, mock_search, mock_container_create,
                           mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y

        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')

        response = self.post('/v1/containers?run=true',
                             params=params,
                             content_type='application/json')

        self.assertEqual(202, response.status_int)
        self.assertTrue(mock_container_create.called)
        self.assertTrue(mock_container_create.call_args[1]['run'] is True)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_run_container_wrong_run_value(self, mock_search,
                                           mock_container_create):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for query parameters"):
            self.post('/v1/containers?run=xyz', params=params,
                      content_type='application/json')

    @patch('zun.compute.api.API.container_create')
    def test_run_container_wrong_memory_value(self, mock_container_create):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "3",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for field .*memory"):
            self.post('/v1/containers?run=true', params=params,
                      content_type='application/json')

    @patch('zun.compute.api.API.container_create')
    def test_run_container_wrong_cpu_value(self, mock_container_create):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512", "cpu": "100",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for field .*cpu"):
            self.post('/v1/containers?run=true', params=params,
                      content_type='application/json')

    @patch('zun.compute.api.API.container_create')
    def test_run_container_wrong_disk_value(self, mock_container_create):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512", "disk": "0",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for field .*disk"):
            self.post('/v1/containers?run=true', params=params,
                      content_type='application/json')

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    def test_run_container_wrong_exposed_ports(
            self, mock_container_create, mock_mock_neutron_get_network):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"exposed_ports": {"foo": {}}}')
        with self.assertRaisesRegex(AppError,
                                    "value foo is invalid as publish port"):
            self.post('/v1/containers?run=true', params=params,
                      content_type='application/json')

        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"exposed_ports": {"80/foo": {}}}')
        with self.assertRaisesRegex(AppError,
                                    "value foo is an invalid protocol"):
            self.post('/v1/containers?run=true', params=params,
                      content_type='application/json')

    def test_run_container_runtime_wrong_api_version(self):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": "env", "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"runtime": "runc"}')
        headers = {"OpenStack-API-Version": "container 1.4"}
        with self.assertRaisesRegex(AppError,
                                    "Invalid param runtime"):
            self.post('/v1/containers?run=true',
                      params=params, content_type='application/json',
                      headers=headers)

    def test_run_container_runtime_wrong_value(self):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"runtime": 1234}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for field"):
            self.post('/v1/containers?run=true',
                      params=params, content_type='application/json')

    def test_run_container_with_hostname_wrong_api_version(self):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": "env", "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"hostname": "testhost"}')
        headers = {"OpenStack-API-Version": "container 1.7"}
        with self.assertRaisesRegex(AppError,
                                    "Invalid param hostname"):
            self.post('/v1/containers?run=true',
                      params=params, content_type='application/json',
                      headers=headers)

    def test_run_container_healthcheck_wrong_value(self):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"healthcheck": "test"}')
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for field"):
            self.post('/v1/containers?run=true',
                      params=params, content_type='application/json')

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_run_container_with_false(self, mock_search,
                                      mock_container_create,
                                      mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y

        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        response = self.post('/v1/containers?run=false',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        self.assertTrue(mock_container_create.called)
        self.assertTrue(mock_container_create.call_args[1]['run'] is False)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_run_container_with_wrong(self, mock_search,
                                      mock_container_create):
        mock_container_create.side_effect = exception.InvalidValue
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        self.assertRaises(AppError, self.post, '/v1/containers?run=wrong',
                          params=params, content_type='application/json')
        self.assertTrue(mock_container_create.assert_not_called)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container(self, mock_search, mock_container_create,
                              mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')

        self.assertEqual(202, response.status_int)
        self.assertNotIn('host', response.json)
        self.assertTrue(mock_container_create.called)
        self.assertTrue(mock_container_create.call_args[1]['run'] is False)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.common.context.RequestContext.can')
    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_by_admin(
            self, mock_search, mock_container_create, mock_neutron_get_network,
            mock_can):
        mock_container_create.side_effect = lambda x, y, **z: y
        mock_can.return_value = True
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')

        self.assertEqual(202, response.status_int)
        self.assertIn('host', response.json)
        self.assertTrue(mock_container_create.called)
        self.assertTrue(mock_container_create.call_args[1]['run'] is False)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.compute.api.API.container_create')
    def test_create_container_image_not_specified(self, mock_container_create):

        params = ('{"name": "MyDocker",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        with self.assertRaisesRegex(AppError,
                                    "is a required property"):
            self.post('/v1/containers/',
                      params=params,
                      content_type='application/json')
        self.assertTrue(mock_container_create.assert_not_called)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_set_project_id_and_user_id(
            self, mock_search, mock_container_create,
            mock_neutron_get_network):
        def _create_side_effect(cnxt, container, **kwargs):
            self.assertEqual(self.context.project_id, container.project_id)
            self.assertEqual(self.context.user_id, container.user_id)
            return container
        mock_container_create.side_effect = _create_side_effect

        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        self.post('/v1/containers/',
                  params=params,
                  content_type='application/json')
        mock_neutron_get_network.assert_called_once()

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_resp_has_status_reason(self, mock_search,
                                                     mock_container_create,
                                                     mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        self.assertIn('status_reason', response.json.keys())
        mock_neutron_get_network.assert_called_once()

    @patch('zun.common.policy.enforce')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.container_delete')
    @patch('zun.volume.cinder_api.CinderAPI.search_volume')
    @patch('zun.volume.cinder_api.CinderAPI.ensure_volume_usable')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_full_params(self, mock_search,
                                               mock_ensure_volume_usable,
                                               mock_search_volume,
                                               mock_container_delete,
                                               mock_container_create,
                                               mock_neutron_get_network,
                                               mock_policy):
        mock_policy.return_value = True
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        fake_volume_id = 'fakevolid'
        fake_volume = mock.Mock(id=fake_volume_id)
        mock_search_volume.return_value = fake_volume
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"runtime": "runc", "hostname": "testhost",'
                  '"disk": 20, "restart_policy": {"Name": "no"},'
                  '"nets": [{"network": "testpublicnet"}],'
                  '"exposed_ports": {"80/tcp": {}, "22": {}},'
                  '"mounts": [{"source": "s", "destination": "d"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual({"key1": "val1", "key2": "val2"},
                         c.get('environment'))
        self.assertEqual('testhost', c.get('hostname'))
        self.assertEqual(20, c.get('disk'))
        self.assertEqual({"Name": "no", "MaximumRetryCount": "0"},
                         c.get('restart_policy'))
        self.assertNotIn('host', c)
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])
        requested_volumes = \
            mock_container_create.call_args[1]['requested_volumes']
        self.assertEqual(1, len(requested_volumes))
        self.assertEqual(
            fake_volume_id,
            next(iter(requested_volumes.values()))[0].cinder_volume_id)
        exposed_ports = mock_container_create.call_args[0][1].exposed_ports
        self.assertEqual(2, len(exposed_ports))
        self.assertIn("80/tcp", exposed_ports)
        self.assertIn("22/tcp", exposed_ports)

        # Delete the container we created
        def side_effect(*args, **kwargs):
            (ctx, cnt, force) = args
            cnt.destroy(ctx)

        mock_container_delete.side_effect = side_effect
        response = self.delete(
            '/v1/containers/%s?force=True' % c.get('uuid'))
        self.assertEqual(204, response.status_int)

        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        c = response.json['containers']
        self.assertEqual(0, len(c))
        self.assertTrue(mock_container_create.called)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_minimum_params(self,
                                                  mock_search,
                                                  mock_container_create,
                                                  mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"image": "ubuntu"}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertIsNotNone(c.get('name'))
        self.assertFalse(c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual(1.0, c.get('cpu'))
        # TODO(kiennt): Uncomment it when bug [1] be resolved.
        #               At this time, limit disk size feature will be ready.
        # [1] https://bugs.launchpad.net/zun/+bug/1746401
        # self.assertEqual(10, c.get('disk'))
        self.assertEqual({}, c.get('environment'))
        self.assertIsNone(c.get('runtime'))
        self.assertIsNone(c.get('hostname'))
        self.assertEqual({}, c.get('restart_policy'))
        self.assertNotIn('host', c)
        mock_neutron_get_network.assert_called_once()
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_availability_zone(
            self, mock_search, mock_container_create,
            mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"],'
                  '"availability_zone": "test-az"}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual(1.0, c.get('cpu'))
        # TODO(kiennt): Uncomment it when bug [1] be resolved.
        #               At this time, limit disk size feature will be ready.
        # [1] https://bugs.launchpad.net/zun/+bug/1746401
        # self.assertEqual(10, c.get('disk'))
        self.assertEqual({}, c.get('environment'))
        self.assertNotIn('host', c)
        mock_neutron_get_network.assert_called_once()
        extra_spec = \
            mock_container_create.call_args[1]['extra_spec']
        self.assertEqual('test-az', extra_spec['availability_zone'])

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_restart_policy_no_retry_0(
            self,
            mock_search,
            mock_container_create,
            mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"restart_policy": {"Name": "no",'
                  '"MaximumRetryCount": "0"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual({"Name": "no", "MaximumRetryCount": "0"},
                         c.get('restart_policy'))
        self.assertNotIn('host', c)
        mock_neutron_get_network.assert_called_once()
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_restart_policy_no_retry_6(
            self,
            mock_search,
            mock_container_create,
            mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"restart_policy": {"Name": "no",'
                  '"MaximumRetryCount": "6"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual({"Name": "no", "MaximumRetryCount": "0"},
                         c.get('restart_policy'))
        self.assertNotIn('host', c)
        mock_neutron_get_network.assert_called_once()
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_restart_policy_unless_stopped(
            self,
            mock_search,
            mock_container_create,
            mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"restart_policy": {"Name": "unless-stopped",'
                  '"MaximumRetryCount": "0"}}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual({"Name": "unless-stopped", "MaximumRetryCount": "0"},
                         c.get('restart_policy'))
        self.assertNotIn('host', c)
        mock_neutron_get_network.assert_called_once()
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])

    @patch('zun.common.policy.enforce')
    @patch('neutronclient.v2_0.client.Client.show_port')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_port')
    @patch('zun.network.neutron.NeutronAPI.ensure_neutron_port_usable')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.container_delete')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_requested_neutron_port(
            self, mock_search, mock_container_delete, mock_container_create,
            mock_ensure_port_usable, mock_get_port,
            mock_get_network, mock_show_port, mock_policy):
        mock_policy.return_value = True
        mock_container_create.side_effect = lambda x, y, **z: y
        fake_port = {'network_id': 'foo', 'id': 'bar'}
        fake_private_network = {'router:external': False, 'shared': False}
        mock_get_port.return_value = fake_port
        mock_get_network.return_value = fake_private_network
        mock_show_port.return_value = {'port': fake_port}
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"nets": [{"port": "testport"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('512', c.get('memory'))
        self.assertEqual({"key1": "val1", "key2": "val2"},
                         c.get('environment'))
        self.assertNotIn('host', c)
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_port['network_id'],
                         requested_networks[0]['network'])
        self.assertEqual(fake_port['id'], requested_networks[0]['port'])
        self.assertTrue(requested_networks[0]['preserve_on_delete'])

        def side_effect(*args, **kwargs):
            (ctx, cnt, force) = args
            cnt.destroy(ctx)

        # Delete the container we created
        mock_container_delete.side_effect = side_effect
        response = self.delete(
            '/v1/containers/%s?force=True' % c.get('uuid'))
        self.assertEqual(204, response.status_int)

        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        c = response.json['containers']
        self.assertEqual(0, len(c))
        self.assertTrue(mock_container_create.called)

    @patch('zun.compute.api.API.container_create')
    @patch('zun.common.context.RequestContext.can')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.network.neutron.NeutronAPI.ensure_neutron_port_usable')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_public_network(
            self, mock_search, mock_ensure_port_usable, mock_get_network,
            mock_authorize, mock_container_create):
        fake_public_network = {'id': 'fakepubnetid',
                               'router:external': True,
                               'shared': False}
        mock_get_network.return_value = fake_public_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"nets": [{"network": "testpublicnet"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        fake_admin_authorize = True
        mock_authorize.return_value = fake_admin_authorize
        self.assertEqual(202, response.status_int)

        fake_not_admin_authorize = False
        mock_authorize.return_value = fake_not_admin_authorize
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json',
                             expect_errors=True)
        self.assertEqual(403, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(
            "It is not allowed to create an interface on external network %s" %
            fake_public_network['id'], response.json['errors'][0]['detail'])
        self.assertTrue(mock_container_create.assert_not_called)

    @patch('zun.compute.api.API.container_create')
    @patch('zun.common.context.RequestContext.can')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.network.neutron.NeutronAPI.ensure_neutron_port_usable')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_ip_addr(
            self, mock_search, mock_ensure_port_usable, mock_get_network,
            mock_authorize, mock_container_create):
        fake_network = {'id': 'fakenetid'}
        mock_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"nets": [{"network": "fakenetid", "v4-fixed-ip": '
                  '"10.0.0.10"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        fake_admin_authorize = True
        mock_authorize.return_value = fake_admin_authorize
        self.assertEqual(202, response.status_int)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_show')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.common.context.RequestContext.can')
    @patch('zun.volume.cinder_api.CinderAPI.create_volume')
    @patch('zun.volume.cinder_api.CinderAPI.ensure_volume_usable')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_create_new_volume(
            self, mock_search, mock_ensure_volume_usable, mock_create_volume,
            mock_authorize, mock_container_create, mock_container_show,
            mock_neutron_get_network):
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        fake_volume_id = 'fakevolid'
        fake_volume = mock.Mock(id=fake_volume_id)
        mock_create_volume.return_value = fake_volume
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"mounts": [{"destination": "d", '
                  '"size": "5"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        # get all containers
        container = objects.Container.list(self.context)[0]
        container.status = 'Creating'
        mock_container_show.return_value = container
        response = self.app.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('Creating', c.get('status'))
        self.assertEqual('512', c.get('memory'))
        self.assertIn('host', c)
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])
        mock_create_volume.assert_called_once()
        mock_ensure_volume_usable.assert_called_once()
        requested_volumes = \
            mock_container_create.call_args[1]['requested_volumes']
        self.assertEqual(1, len(requested_volumes))
        self.assertEqual(
            fake_volume_id,
            requested_volumes[container.uuid][0].cinder_volume_id)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_show')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.common.context.RequestContext.can')
    @patch('zun.volume.cinder_api.CinderAPI.create_volume')
    @patch('zun.volume.cinder_api.CinderAPI.ensure_volume_usable')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_injected_file(
            self, mock_search, mock_ensure_volume_usable, mock_create_volume,
            mock_authorize, mock_container_create, mock_container_show,
            mock_neutron_get_network):
        fake_network = {'id': 'foo'}
        mock_neutron_get_network.return_value = fake_network
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"mounts": [{"destination": "d", "source": "hello",'
                  '            "type": "bind"}]}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        # get all containers
        container = objects.Container.list(self.context)[0]
        container.status = 'Creating'
        mock_container_show.return_value = container
        response = self.app.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        self.assertEqual(2, len(response.json))
        c = response.json['containers'][0]
        self.assertIsNotNone(c.get('uuid'))
        self.assertEqual('MyDocker', c.get('name'))
        self.assertEqual(["env"], c.get('command'))
        self.assertEqual('Creating', c.get('status'))
        self.assertEqual('512', c.get('memory'))
        self.assertIn('host', c)
        requested_networks = \
            mock_container_create.call_args[1]['requested_networks']
        self.assertEqual(1, len(requested_networks))
        self.assertEqual(fake_network['id'], requested_networks[0]['network'])
        mock_create_volume.assert_not_called()
        mock_ensure_volume_usable.assert_not_called()
        requested_volumes = \
            mock_container_create.call_args[1]['requested_volumes']
        self.assertEqual(1, len(requested_volumes))
        self.assertIsNone(
            requested_volumes[container.uuid][0].cinder_volume_id)
        self.assertEqual(
            'local', requested_volumes[container.uuid][0].volume_provider)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_show')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_with_restart_policy_always_and_retrycount(
            self,
            mock_search,
            mock_container_create,
            mock_container_show,
            mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"restart_policy": {"Name": "always",'
                  '"MaximumRetryCount": "1"}}')
        with self.assertRaisesRegex(
                AppError, "maximum retry count not valid with"):
            self.post('/v1/containers/',
                      params=params,
                      content_type='application/json')
        self.assertTrue(mock_container_create.assert_not_called)
        mock_neutron_get_network.assert_called_once()

    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_invalid_long_name(self, mock_search,
                                                mock_container_create):
        # Long name
        params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512"}')
        self.assertRaises(AppError, self.post, '/v1/containers/',
                          params=params, content_type='application/json')
        self.assertTrue(mock_container_create.assert_not_called)

    @patch('zun.objects.Container.list')
    def test_get_all_containers(self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/')

        mock_container_list.assert_called_once_with(mock.ANY,
                                                    1000, None, 'id', 'asc',
                                                    filters={})
        context = mock_container_list.call_args[0][0]
        self.assertIs(False, context.all_projects)
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))
        self.assertNotIn('host', actual_containers[0])

    @patch('zun.common.context.RequestContext.can')
    @patch('zun.objects.Container.list')
    def test_get_all_containers_by_admin(self, mock_container_list, mock_can):
        mock_can.return_value = True
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/')

        mock_container_list.assert_called_once_with(mock.ANY,
                                                    1000, None, 'id', 'asc',
                                                    filters={})
        context = mock_container_list.call_args[0][0]
        self.assertIs(False, context.all_projects)
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))
        self.assertIn('host', actual_containers[0])

    @patch('zun.common.policy.enforce')
    @patch('zun.objects.Container.list')
    def test_get_all_containers_all_projects(self, mock_container_list,
                                             mock_policy):
        mock_policy.return_value = True
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/?all_projects=1')

        mock_container_list.assert_called_once_with(mock.ANY,
                                                    1000, None, 'id', 'asc',
                                                    filters={})
        context = mock_container_list.call_args[0][0]
        self.assertIs(True, context.all_projects)
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))

    @patch('zun.objects.Container.list')
    def test_get_all_has_status_reason(
            self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/')
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))
        self.assertIn('status_reason', actual_containers[0].keys())

    @patch('zun.objects.Container.list')
    def test_get_all_containers_with_pagination_marker(self,
                                                       mock_container_list):
        container_list = []
        for id_ in range(4):
            test_container = utils.create_test_container(
                id=id_, uuid=uuidutils.generate_uuid(),
                name='container' + str(id_), context=self.context)
            container_list.append(objects.Container(self.context,
                                                    **test_container))
        mock_container_list.return_value = container_list[-1:]
        response = self.get('/v1/containers/?limit=3&marker=%s'
                            % container_list[2].uuid)

        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(container_list[-1].uuid,
                         actual_containers[0].get('uuid'))

    @patch('zun.objects.Container.list')
    def test_get_all_containers_with_filter(self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/?name=fake-name')

        mock_container_list.assert_called_once_with(
            mock.ANY, 1000, None, 'id', 'asc', filters={'name': 'fake-name'})
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))

    @patch('zun.objects.Container.list')
    def test_get_all_containers_with_filter_disallow(
            self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/?host=fake-name',
                            expect_errors=True)
        self.assertEqual(403, response.status_int)
        self.assertEqual('application/json', response.content_type)
        rule = 'container:get_one:host'
        self.assertEqual(
            "Policy doesn't allow %s to be performed." % rule,
            response.json['errors'][0]['detail'])

    @patch('zun.objects.Container.list')
    def test_get_all_containers_with_unknown_parameter(
            self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers

        response = self.get('/v1/containers/?unknown=fake-name',
                            expect_errors=True)

        mock_container_list.assert_not_called()
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual("Unknown parameters: unknown",
                         response.json['errors'][0]['detail'])

    @patch('zun.objects.Container.list')
    def test_get_all_containers_with_exception(self, mock_container_list):
        test_container = utils.get_test_container()
        containers = [objects.Container(self.context, **test_container)]
        mock_container_list.return_value = containers
        response = self.get('/v1/containers/')
        mock_container_list.assert_called_once_with(mock.ANY,
                                                    1000, None, 'id', 'asc',
                                                    filters={})
        self.assertEqual(200, response.status_int)
        actual_containers = response.json['containers']
        self.assertEqual(1, len(actual_containers))
        self.assertEqual(test_container['uuid'],
                         actual_containers[0].get('uuid'))

    @patch('zun.compute.api.API.container_show')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_one_by_uuid(self, mock_container_get_by_uuid,
                             mock_container_show):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        mock_container_show.return_value = test_container_obj

        response = self.get('/v1/containers/%s/' % test_container['uuid'])

        mock_container_get_by_uuid.assert_called_once_with(
            mock.ANY,
            test_container['uuid'])
        context = mock_container_get_by_uuid.call_args[0][0]
        self.assertIs(False, context.all_projects)
        self.assertEqual(200, response.status_int)
        self.assertEqual(test_container['uuid'],
                         response.json['uuid'])
        self.assertNotIn('host', response.json)

    @patch('zun.common.context.RequestContext.can')
    @patch('zun.compute.api.API.container_show')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_one_by_admin(self, mock_container_get_by_uuid,
                              mock_container_show, mock_can):
        mock_can.return_value = True
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        mock_container_show.return_value = test_container_obj

        response = self.get('/v1/containers/%s/' % test_container['uuid'])

        mock_container_get_by_uuid.assert_called_once_with(
            mock.ANY,
            test_container['uuid'])
        context = mock_container_get_by_uuid.call_args[0][0]
        self.assertIs(False, context.all_projects)
        self.assertEqual(200, response.status_int)
        self.assertEqual(test_container['uuid'],
                         response.json['uuid'])
        self.assertIn('host', response.json)

    @patch('zun.common.policy.enforce')
    @patch('zun.compute.api.API.container_show')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_one_by_uuid_all_projects(self, mock_container_get_by_uuid,
                                          mock_container_show, mock_policy):
        mock_policy.return_value = True
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        mock_container_show.return_value = test_container_obj

        response = self.get('/v1/containers/%s/?all_projects=1' %
                            test_container['uuid'])

        mock_container_get_by_uuid.assert_called_once_with(
            mock.ANY,
            test_container['uuid'])
        context = mock_container_get_by_uuid.call_args[0][0]
        self.assertIs(True, context.all_projects)
        self.assertEqual(200, response.status_int)
        self.assertEqual(test_container['uuid'],
                         response.json['uuid'])

    @patch('zun.objects.ComputeNode.get_by_name')
    @patch('zun.compute.api.API.container_update')
    @patch('zun.objects.Container.get_by_uuid')
    def test_patch_by_uuid(self, mock_container_get_by_uuid, mock_update,
                           mock_computenode):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        mock_update.return_value = test_container_obj
        test_host = utils.get_test_compute_node()
        numa = objects.numa.NUMATopology._from_dict(test_host['numa_topology'])
        test_host['numa_topology'] = numa
        test_host_obj = objects.ComputeNode(self.context, **test_host)
        mock_computenode.return_value = test_host_obj
        params = {'cpu': 1}
        container_uuid = test_container.get('uuid')
        response = self.patch_json(
            '/containers/%s/' % container_uuid,
            params=params)

        self.assertEqual(200, response.status_int)
        self.assertTrue(mock_update.called)
        self.assertNotIn('host', response.json)

    @patch('zun.common.context.RequestContext.can')
    @patch('zun.objects.ComputeNode.get_by_name')
    @patch('zun.compute.api.API.container_update')
    @patch('zun.objects.Container.get_by_uuid')
    def test_patch_by_admin(self, mock_container_get_by_uuid, mock_update,
                            mock_computenode, mock_can):
        mock_can.return_value = True
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        mock_update.return_value = test_container_obj
        test_host = utils.get_test_compute_node()
        numa = objects.numa.NUMATopology._from_dict(test_host['numa_topology'])
        test_host['numa_topology'] = numa
        test_host_obj = objects.ComputeNode(self.context, **test_host)
        mock_computenode.return_value = test_host_obj
        params = {'cpu': 1}
        container_uuid = test_container.get('uuid')
        response = self.patch_json(
            '/containers/%s/' % container_uuid,
            params=params)

        self.assertEqual(200, response.status_int)
        self.assertTrue(mock_update.called)
        self.assertIn('host', response.json)

    def _action_test(self, test_container_obj, action, ident_field,
                     mock_container_action, status_code, query_param=''):
        ident = test_container_obj.uuid
        get_by_ident_loc = 'zun.objects.Container.get_by_%s' % ident_field
        with patch(get_by_ident_loc) as mock_get_by_indent:
            mock_get_by_indent.return_value = test_container_obj
            response = self.post('/v1/containers/%s/%s/?%s' %
                                 (ident, action, query_param))
            self.assertEqual(status_code, response.status_int)

            # Only PUT should work, others like GET should fail
            self.assertRaises(AppError, self.get,
                              ('/v1/containers/%s/%s/' %
                               (ident, action)))
        if query_param:
            value = query_param.split('=')[1]
            mock_container_action.assert_called_once_with(
                mock.ANY, test_container_obj, value)
        else:
            mock_container_action.assert_called_once_with(
                mock.ANY, test_container_obj)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_start')
    def test_start_by_uuid(self, mock_container_start, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        self._action_test(test_container_obj, 'start', 'uuid',
                          mock_container_start, 202)

    def test_start_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Running')
        with self.assertRaisesRegex(
                AppError, "Cannot start container %s in Running state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'start'))

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_stop')
    def test_stop_by_uuid(self, mock_container_stop, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        self._action_test(test_container_obj, 'stop', 'uuid',
                          mock_container_stop, 202,
                          query_param='timeout=10')

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_stop')
    def test_stop_by_name_invalid_timeout_value(self,
                                                mock_container_stop,
                                                mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for query parameters"):
            self._action_test(test_container_obj, 'stop', 'name',
                              mock_container_stop, 202,
                              query_param='timeout=xyz')

    def test_stop_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Stopped')
        with self.assertRaisesRegex(
                AppError, "Cannot stop container %s in Stopped state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'stop'))

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_pause')
    def test_pause_by_uuid(self, mock_container_pause, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        self._action_test(test_container_obj, 'pause', 'uuid',
                          mock_container_pause, 202)

    def test_pause_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Stopped')
        with self.assertRaisesRegex(
                AppError, "Cannot pause container %s in Stopped state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'pause'))

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_unpause')
    def test_unpause_by_uuid(self, mock_container_unpause, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        self._action_test(test_container_obj, 'unpause', 'uuid',
                          mock_container_unpause, 202)

    def test_unpause_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Running')
        with self.assertRaisesRegex(
                AppError,
                "Cannot unpause container %s in Running state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'unpause'))

    @patch('zun.compute.api.API.container_rebuild')
    @patch('zun.common.policy.enforce')
    def test_rebuild_container(self, mock_policy, mock_rebuild):
        mock_policy.return_value = True
        uuid = uuidutils.generate_uuid()
        utils.create_test_container(context=self.context,
                                    uuid=uuid, name="container")
        url = '/v1/containers/%s/rebuild' % uuid
        response = self.post(url)
        self.assertEqual(202, response.status_int)
        mock_rebuild.assert_called_once()

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_reboot')
    def test_reboot_by_uuid(self, mock_container_reboot, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        with patch.object(test_container_obj, 'save') as mock_save:
            self._action_test(test_container_obj, 'reboot', 'uuid',
                              mock_container_reboot, 202,
                              query_param='timeout=10')
            mock_save.assert_called_once()

    def test_reboot_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Paused')
        with self.assertRaisesRegex(
                AppError, "Cannot reboot container %s in Paused state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'reboot'))

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_reboot')
    def test_reboot_by_name_wrong_timeout_value(self, mock_container_reboot,
                                                mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        with self.assertRaisesRegex(AppError,
                                    "Invalid input for query parameters"):
            self._action_test(test_container_obj, 'reboot', 'name',
                              mock_container_reboot, 202,
                              query_param='timeout=xyz')

    @patch('zun.compute.api.API.container_logs')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_logs_by_uuid(self, mock_get_by_uuid, mock_container_logs):
        mock_container_logs.return_value = "test"
        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')
        response = self.get('/v1/containers/%s/logs/' % container_uuid)

        self.assertEqual(200, response.status_int)
        mock_container_logs.assert_called_once_with(
            mock.ANY, test_container_obj, True, True, False, 'all', None)

    @patch('zun.compute.api.API.container_logs')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_logs_with_options_by_uuid(self, mock_get_by_uuid,
                                           mock_container_logs):
        mock_container_logs.return_value = "test"
        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')
        response = self.get(
            '/v1/containers/%s/logs?stderr=True&stdout=True'
            '&timestamps=False&tail=1&since=100000000' % container_uuid)
        self.assertEqual(200, response.status_int)
        mock_container_logs.assert_called_once_with(
            mock.ANY, test_container_obj, True, True, False, '1', '100000000')

    @patch('zun.compute.api.API.container_logs')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_logs_put_fails(self, mock_get_by_uuid, mock_container_logs):
        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')
        self.assertRaises(AppError, self.post,
                          '/v1/containers/%s/logs/' % container_uuid)
        self.assertFalse(mock_container_logs.called)

    @patch('zun.compute.api.API.container_logs')
    @patch('zun.objects.Container.get_by_uuid')
    def test_get_logs_with_invalid_since(self, mock_get_by_uuid,
                                         mock_container_logs):
        invalid_sinces = ['x11', '11x', '2000-01-01 01:01:01']
        for value in invalid_sinces:
            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')
            params = {'since': value}

            self.assertRaises(AppError, self.post,
                              '/v1/containers/%s/logs' %
                              container_uuid, params)
            self.assertFalse(mock_container_logs.called)

    def test_get_logs_with_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Creating')
        with self.assertRaisesRegex(
                AppError,
                "Cannot logs container %s in Creating state" % uuid):
            self.get('/v1/containers/%s/logs/' % test_object.uuid)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_exec')
    @patch('zun.objects.Container.get_by_uuid')
    def test_execute_command_by_uuid(self, mock_get_by_uuid,
                                     mock_container_exec, mock_validate):
        mock_container_exec.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, 'execute')
        cmd = {'command': 'ls'}
        response = self.post(url, cmd)
        self.assertEqual(200, response.status_int)
        mock_container_exec.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['command'], True, False)

    def test_exec_command_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Stopped')
        cmd = {'command': 'ls'}
        with self.assertRaisesRegex(
                AppError,
                "Cannot execute container %s in Stopped state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'execute'), cmd)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_exec')
    @patch('zun.objects.Container.get_by_uuid')
    def test_execute_without_command_by_uuid(self, mock_get_by_uuid,
                                             mock_container_exec,
                                             mock_validate):
        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')
        cmd = {'command': ''}
        self.assertRaises(AppError, self.post,
                          '/v1/containers/%s/execute' %
                          container_uuid, cmd)
        self.assertFalse(mock_container_exec.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_delete')
    @patch('zun.objects.Container.get_by_uuid')
    def test_delete_container_by_uuid(self, mock_get_by_uuid,
                                      mock_container_delete, mock_validate):
        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')
        response = self.delete('/v1/containers/%s/' % container_uuid)

        self.assertEqual(204, response.status_int)
        mock_container_delete.assert_called_once_with(
            mock.ANY, test_container_obj, False)
        context = mock_container_delete.call_args[0][0]
        self.assertIs(False, context.all_projects)

    @patch('zun.common.policy.enforce')
    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_delete')
    @patch('zun.objects.Container.get_by_uuid')
    def test_delete_container_by_uuid_all_projects(self, mock_get_by_uuid,
                                                   mock_container_delete,
                                                   mock_validate, mock_policy):
        mock_policy.return_value = True
        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')
        response = self.delete('/v1/containers/%s/?all_projects=1' %
                               container_uuid)

        self.assertEqual(204, response.status_int)
        mock_container_delete.assert_called_once_with(
            mock.ANY, test_container_obj, False)
        context = mock_container_delete.call_args[0][0]
        self.assertIs(True, context.all_projects)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_stop')
    @patch('zun.compute.api.API.container_delete')
    @patch('zun.objects.Container.get_by_uuid')
    def test_delete_container_by_uuid_with_stop(self, mock_get_by_uuid,
                                                mock_container_stop,
                                                mock_container_delete,
                                                mock_validate):
        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')
        response = self.delete('/v1/containers/%s?stop=True' %
                               container_uuid)

        self.assertEqual(204, response.status_int)

    def test_delete_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Running')
        with self.assertRaisesRegex(
                AppError,
                "Cannot delete container %s in Running state" % uuid):
            self.delete('/v1/containers/%s' % (test_object.uuid))

    @patch('zun.common.policy.enforce')
    def test_delete_force_by_uuid_invalid_state(self, mock_policy):
        mock_policy.return_value = True
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Paused')
        with self.assertRaisesRegex(
                AppError,
                "Cannot delete_force container %s in Paused state" % uuid):
            self.delete('/v1/containers/%s?force=True' % test_object.uuid)

    @patch('zun.common.policy.enforce')
    @patch('zun.compute.api.API.container_delete')
    def test_delete_by_uuid_invalid_state_force_true(self, mock_delete,
                                                     mock_policy):
        mock_policy.return_value = True
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Running')
        response = self.delete('/v1/containers/%s?force=True' % (
            test_object.uuid))
        self.assertEqual(204, response.status_int)

    @patch('zun.compute.api.API.container_delete')
    def test_delete_by_uuid_with_force_wrong(self, mock_delete):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid)
        mock_delete.side_effect = exception.InvalidValue
        self.assertRaises(AppError, self.delete,
                          '/v1/containers/%s?force=wrong' % test_object.uuid)
        self.assertTrue(mock_delete.assert_not_called)

    def test_delete_container_with_uuid_not_found(self):
        uuid = uuidutils.generate_uuid()
        self.assertRaises(AppError, self.delete,
                          '/v1/containers/%s' % uuid)

    @patch('zun.objects.Container.destroy')
    @patch('zun.common.utils.validate_container_state')
    @patch('zun.objects.Container.get_by_uuid')
    def test_delete_container_without_host(self, mock_get_by_uuid,
                                           mock_validate, mock_destroy):
        test_container = utils.get_test_container(host="")
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_by_uuid.return_value = test_container_obj
        container_uuid = test_container.get('uuid')
        response = self.delete('/v1/containers/%s/' % container_uuid)
        self.assertEqual(204, response.status_int)
        mock_validate.assert_called_once()
        mock_destroy.assert_called_once()

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_kill')
    @patch('zun.objects.Container.get_by_uuid')
    def test_kill_container_by_uuid(self,
                                    mock_get_by_uuid, mock_container_kill,
                                    mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        mock_container_kill.return_value = test_container_obj
        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, 'kill')
        cmd = {'signal': '9'}
        response = self.post(url, cmd)
        self.assertEqual(202, response.status_int)
        mock_container_kill.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['signal'])

    def test_kill_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Stopped')
        body = {'signal': 9}
        with self.assertRaisesRegex(
                AppError, "Cannot kill container %s in Stopped state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'kill'), body)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_kill')
    @patch('zun.objects.Container.get_by_uuid')
    def test_kill_container_which_not_exist(self,
                                            mock_get_by_uuid,
                                            mock_container_kill,
                                            mock_validate):
        mock_container_kill.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
        mock_container_kill.side_effect = Exception

        container_uuid = "edfe2a25-2901-438d-8157-fffffd68d051"
        self.assertRaises(AppError, self.post,
                          '/v1/containers/%s/%s/' % (container_uuid, 'kill'))
        self.assertTrue(mock_container_kill.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_kill')
    @patch('zun.objects.Container.get_by_uuid')
    def test_kill_container_with_exception(self,
                                           mock_get_by_uuid,
                                           mock_container_kill,
                                           mock_validate):
        mock_container_kill.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
        mock_container_kill.side_effect = Exception

        container_uuid = test_container.get('uuid')
        self.assertRaises(AppError, self.post,
                          '/v1/containers/%s/%s/' % (container_uuid, 'kill'))
        self.assertTrue(mock_container_kill.called)

    @patch('zun.compute.api.API.container_kill')
    @patch('zun.objects.Container.get_by_uuid')
    def test_kill_container_with_invalid_singal(self,
                                                mock_get_by_uuid,
                                                mock_container_kill):
        invalid_signal = ['11x', 'x11']
        for value in invalid_signal:
            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')
            params = {'signal': value}
            with self.assertRaisesRegex(
                    AppError, "Bad response: 400 Bad Request"):
                self.post('/v1/containers/%s/kill/' %
                          container_uuid, params)
            self.assertFalse(mock_container_kill.called)

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.container_create')
    @patch('zun.compute.api.API.image_search')
    def test_create_container_resp_has_image_driver(self, mock_search,
                                                    mock_container_create,
                                                    mock_neutron_get_network):
        mock_container_create.side_effect = lambda x, y, **z: y
        # Create a container with a command
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512",'
                  '"environment": {"key1": "val1", "key2": "val2"},'
                  '"image_driver": "glance"}')
        response = self.post('/v1/containers/',
                             params=params,
                             content_type='application/json')
        self.assertEqual(202, response.status_int)
        self.assertIn('image_driver', response.json.keys())
        self.assertEqual('glance', response.json.get('image_driver'))

    @patch('zun.compute.api.API.container_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_attach_container_by_uuid(self, mock_get_by_uuid,
                                      mock_container_attach):
        mock_container_attach.return_value = "ws://test"
        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')
        response = self.get('/v1/containers/%s/attach/' % container_uuid)

        self.assertEqual(200, response.status_int)
        mock_container_attach.assert_called_once_with(
            mock.ANY, test_container_obj)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_attach_container_with_exception(self,
                                             mock_get_by_uuid,
                                             mock_container_attach,
                                             mock_validate):
        mock_container_attach.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
        mock_container_attach.side_effect = Exception

        container_uuid = test_container.get('uuid')
        self.assertRaises(AppError, self.get,
                          '/v1/containers/%s/attach/' % container_uuid)
        self.assertTrue(mock_container_attach.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_resize')
    @patch('zun.objects.Container.get_by_name')
    def test_resize_by_uuid(self, mock_get_by_uuid, mock_container_resize,
                            mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        mock_container_resize.return_value = test_container_obj
        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_name = test_container.get('name')
        url = '/v1/containers/%s/%s/' % (container_name, 'resize')
        cmd = {'h': '100', 'w': '100'}
        response = self.post(url, cmd)
        self.assertEqual(200, response.status_int)
        mock_container_resize.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['h'], cmd['w'])

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_resize')
    @patch('zun.objects.Container.get_by_uuid')
    def test_resize_with_exception(self, mock_get_by_uuid,
                                   mock_container_resize, mock_validate):
        mock_container_resize.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
        mock_container_resize.side_effect = Exception

        container_uuid = test_container.get('uuid')
        body = {'h': '100', 'w': '100'}
        self.assertRaises(AppError, self.post,
                          '/v1/containers/%s/%s/' %
                          (container_uuid, 'resize'), body)
        self.assertTrue(mock_container_resize.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.resize_container')
    @patch('zun.objects.Container.get_by_name')
    def test_resize_container(self, mock_get_by_uuid,
                              mock_resize_container, mock_validate):
        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        mock_resize_container.return_value = test_container_obj
        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_name = test_container.get('name')
        url = '/v1/containers/%s/resize_container/' % container_name
        params = {'cpu': 1, 'memory': '512'}
        response = self.post(url, params)
        self.assertEqual(202, response.status_int)
        mock_resize_container.assert_called_once_with(
            mock.ANY, test_container_obj, params)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_top')
    @patch('zun.objects.Container.get_by_uuid')
    def test_top_command_by_uuid(self, mock_get_by_uuid,
                                 mock_container_top, mock_validate):
        mock_container_top.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')
        response = self.get('/v1/containers/%s/top?ps_args=aux' %
                            container_uuid)
        self.assertEqual(200, response.status_int)
        self.assertTrue(mock_container_top.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_top')
    @patch('zun.objects.Container.get_by_uuid')
    def test_top_command_invalid_ps(self, mock_get_by_uuid,
                                    mock_container_top, mock_validate):
        mock_container_top.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
        mock_container_top.side_effect = Exception

        container_uuid = test_container.get('uuid')
        self.assertRaises(AppError, self.get,
                          '/v1/containers/%s/top?ps_args=kkkk' %
                          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.get(url, cmd)
        self.assertEqual(200, response.status_int)
        container_get_archive.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['path'], mock.ANY)

    def test_get_archive_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Error')
        with self.assertRaisesRegex(
                AppError,
                "Cannot get_archive container %s in Error state" % uuid):
            self.get('/v1/containers/%s/%s/' % (test_object.uuid,
                                                'get_archive'))

    @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.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'], mock.ANY)

    def test_put_archive_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        test_object = utils.create_test_container(context=self.context,
                                                  uuid=uuid, status='Error')
        with self.assertRaisesRegex(
                AppError,
                "Cannot put_archive container %s in Error state" % uuid):
            self.post('/v1/containers/%s/%s/' % (test_object.uuid,
                                                 'put_archive'))

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_stats')
    @patch('zun.objects.Container.get_by_uuid')
    def test_stats_container_by_uuid(self, mock_get_by_uuid,
                                     mock_container_stats, mock_validate):
        mock_container_stats.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/stats'\
              % container_uuid
        response = self.get(url)
        self.assertEqual(200, response.status_int)
        self.assertTrue(mock_container_stats.called)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_commit')
    @patch('zun.objects.Container.get_by_name')
    def test_commit_by_name(self, mock_get_by_name,
                            mock_container_commit, mock_validate):

        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_by_name.return_value = test_container_obj
        mock_container_commit.return_value = None
        container_name = test_container.get('name')
        url = '/v1/containers/%s/%s/' % (container_name, 'commit')
        cmd = {'repository': 'repo', 'tag': 'tag'}
        response = self.post(url, cmd)
        self.assertEqual(202, response.status_int)
        mock_container_commit.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['repository'], cmd['tag'])

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_commit')
    @patch('zun.objects.Container.get_by_uuid')
    def test_commit_by_uuid(self, mock_get_by_uuid,
                            mock_container_commit, mock_validate):

        test_container_obj = objects.Container(self.context,
                                               **utils.get_test_container())
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_by_uuid.return_value = test_container_obj
        mock_container_commit.return_value = None
        container_uuid = test_container.get('uuid')
        url = '/v1/containers/%s/%s/' % (container_uuid, 'commit')
        cmd = {'repository': 'repo', 'tag': 'tag'}
        response = self.post(url, cmd)
        self.assertEqual(202, response.status_int)
        mock_container_commit.assert_called_once_with(
            mock.ANY, test_container_obj, cmd['repository'], cmd['tag'])

    def test_commit_by_uuid_invalid_state(self):
        uuid = uuidutils.generate_uuid()
        cmd = {'repository': 'repo', 'tag': 'tag'}
        utils.create_test_container(context=self.context,
                                    uuid=uuid, status='Error')
        with self.assertRaisesRegex(
                AppError, "Cannot commit container %s in Error state" % uuid):
            self.post('/v1/containers/%s/commit/' % uuid, cmd)

    @patch('zun.common.utils.validate_container_state')
    @patch('zun.compute.api.API.container_exec_resize')
    @patch('zun.api.utils.get_resource')
    def test_execute_resize_container_exec(
            self, mock_get_resource, mock_exec_resize, mock_validate):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_resource.return_value = test_container_obj
        mock_exec_resize.return_value = None
        container_name = test_container.get('name')
        url = '/v1/containers/%s/%s/' % (container_name, 'execute_resize')
        fake_exec_id = ('7df36611fa1fc855618c2c643835d41d'
                        'ac3fe568e7688f0bae66f7bcb3cccc6c')
        kwargs = {'exec_id': fake_exec_id, 'h': '100', 'w': '100'}
        response = self.post(url, kwargs)
        self.assertEqual(200, response.status_int)
        mock_exec_resize.assert_called_once_with(
            mock.ANY, test_container_obj, fake_exec_id, kwargs['h'],
            kwargs['w'])

    @mock.patch('zun.compute.api.API.add_security_group')
    @mock.patch('zun.network.neutron.NeutronAPI.find_resourceid_by_name_or_id')
    @mock.patch('zun.api.utils.get_resource')
    def test_add_security_group_by_uuid(self, mock_get_resource,
                                        mock_find_resourceid,
                                        mock_add_security_group):
        headers = {"OpenStack-API-Version": "container 1.14"}
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_resource.return_value = test_container_obj
        mock_find_resourceid.return_value = 'fake_security_group_id'
        container_name = test_container.get('name')
        security_group_id_to_add = '5f7cf831-9a9c-4e2b-87b2-6081667f852b'
        url = '/v1/containers/%s/%s?name=%s' % (container_name,
                                                'add_security_group',
                                                security_group_id_to_add)
        response = self.post(url, headers=headers)
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)
        mock_find_resourceid.assert_called_once_with(
            'security_group', security_group_id_to_add, mock.ANY)
        mock_add_security_group.assert_called_once_with(
            mock.ANY, test_container_obj, 'fake_security_group_id')

    @mock.patch('zun.compute.api.API.add_security_group')
    @mock.patch('zun.network.neutron.NeutronAPI.find_resourceid_by_name_or_id')
    @mock.patch('zun.api.utils.get_resource')
    def test_add_security_group_not_found(self, mock_get_resource,
                                          mock_find_resourceid,
                                          mock_add_security_group):
        headers = {"OpenStack-API-Version": "container 1.14"}
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_resource.return_value = test_container_obj
        mock_find_resourceid.side_effect = n_exc.NotFound()
        container_name = test_container.get('name')
        security_group_to_add = '5f7cf831-9a9c-4e2b-87b2-6081667f852b'
        url = '/v1/containers/%s/%s?name=%s' % (container_name,
                                                'add_security_group',
                                                security_group_to_add)
        response = self.post(url, expect_errors=True, headers=headers)
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(
            "Security group %s not found." % security_group_to_add,
            response.json['errors'][0]['detail'])

    @mock.patch('zun.compute.api.API.add_security_group')
    @mock.patch('zun.network.neutron.NeutronAPI.find_resourceid_by_name_or_id')
    @mock.patch('zun.api.utils.get_resource')
    def test_add_security_group_not_unique_match(self, mock_get_resource,
                                                 mock_find_resourceid,
                                                 mock_add_security_group):
        headers = {"OpenStack-API-Version": "container 1.14"}
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_resource.return_value = test_container_obj
        mock_find_resourceid.side_effect = n_exc.NeutronClientNoUniqueMatch()
        container_name = test_container.get('name')
        security_group_to_add = '5f7cf831-9a9c-4e2b-87b2-6081667f852b'
        url = '/v1/containers/%s/%s?name=%s' % (container_name,
                                                'add_security_group',
                                                security_group_to_add)
        response = self.post(url, expect_errors=True, headers=headers)
        self.assertEqual(409, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(
            "Multiple security group matches found for name %s, "
            "use an ID to be more specific." % security_group_to_add,
            response.json['errors'][0]['detail'])

    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.compute.api.API.network_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_attach(self, mock_by_uuid, mock_attach, mock_get_network):
        built_requested_network = {
            'network': 'fake-net-id',
            'port': '',
            'router:external': False,
            'shared': False,
            'fixed_ip': '',
            'preserve_on_delete': False}
        query = 'network=private'
        self._test_network_attach(mock_by_uuid, mock_attach, mock_get_network,
                                  query, built_requested_network)
        mock_get_network.assert_called_once_with('private')

    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.compute.api.API.network_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_attach_with_fixed_ip(self, mock_by_uuid, mock_attach,
                                          mock_get_network):
        built_requested_network = {
            'network': 'fake-net-id',
            'port': '',
            'router:external': False,
            'shared': False,
            'fixed_ip': '10.0.0.3',
            'preserve_on_delete': False}
        query = 'network=private&fixed_ip=10.0.0.3'
        self._test_network_attach(mock_by_uuid, mock_attach, mock_get_network,
                                  query, built_requested_network)
        mock_get_network.assert_called_once_with('private')

    @patch('zun.network.neutron.NeutronAPI.get_neutron_port')
    @patch('zun.network.neutron.NeutronAPI.ensure_neutron_port_usable')
    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.compute.api.API.network_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_attach_with_port(self, mock_by_uuid, mock_attach,
                                      mock_get_network,
                                      mock_ensure, mock_get_port):
        mock_get_port.return_value = {
            'id': 'fake-port-id',
            'network_id': 'fake-net-id',
        }
        built_requested_network = {
            'network': 'fake-net-id',
            'port': 'fake-port-id',
            'router:external': False,
            'shared': False,
            'fixed_ip': '',
            'preserve_on_delete': True}
        query = 'port=fake-port'
        self._test_network_attach(mock_by_uuid, mock_attach, mock_get_network,
                                  query, built_requested_network)
        mock_get_port.assert_called_once_with('fake-port')
        mock_get_network.assert_called_once_with('fake-net-id')

    @patch('zun.network.neutron.NeutronAPI.get_available_network')
    @patch('zun.compute.api.API.network_attach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_attach_without_params(self, mock_by_uuid, mock_attach,
                                           mock_get_network):
        built_requested_network = {
            'network': 'fake-net-id',
            'port': '',
            'router:external': False,
            'shared': False,
            'fixed_ip': '',
            'preserve_on_delete': False}
        query = ''
        self._test_network_attach(mock_by_uuid, mock_attach, mock_get_network,
                                  query, built_requested_network)
        mock_get_network.assert_called_once_with()

    def _test_network_attach(self, mock_by_uuid, mock_attach, mock_get_network,
                             query, built_requested_network):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_by_uuid.return_value = test_container_obj
        container_uuid = test_container.get('uuid')
        fake_network = {'id': 'fake-net-id',
                        'router:external': False,
                        'shared': False}
        mock_get_network.return_value = fake_network
        url = '/v1/containers/%s/%s?%s' % (container_uuid, 'network_attach',
                                           query)
        response = self.post(url)
        self.assertEqual(200, response.status_int)
        mock_attach.assert_called_once_with(mock.ANY, test_container_obj,
                                            built_requested_network)

    @patch('zun.network.neutron.NeutronAPI.get_neutron_network')
    @patch('zun.compute.api.API.network_detach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_detach(self, mock_by_uuid, mock_detach, mock_get_network):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_by_uuid.return_value = test_container_obj
        container_uuid = test_container.get('uuid')
        mock_get_network.return_value = {'id': 'private'}
        mock_detach.return_value = None
        url = '/v1/containers/%s/%s?network=%s' % (container_uuid,
                                                   'network_detach',
                                                   'private')
        response = self.post(url)
        self.assertEqual(202, response.status_int)
        mock_detach.assert_called_once_with(mock.ANY, test_container_obj,
                                            'private')

    @patch('zun.network.neutron.NeutronAPI.get_neutron_port')
    @patch('zun.compute.api.API.network_detach')
    @patch('zun.objects.Container.get_by_uuid')
    def test_network_detach_with_port(self, mock_by_uuid, mock_detach,
                                      mock_get_port):
        port_uuid = uuidutils.generate_uuid()
        network_uuid = uuidutils.generate_uuid()
        test_container = utils.get_test_container(port=port_uuid,
                                                  network=network_uuid)
        test_container_obj = objects.Container(self.context, **test_container)
        mock_by_uuid.return_value = test_container_obj
        container_uuid = test_container.get('uuid')
        mock_get_port.return_value = {'network_id': network_uuid,
                                      'id': port_uuid}
        mock_detach.return_value = None
        url = '/v1/containers/%s/%s?port=%s' % (container_uuid,
                                                'network_detach',
                                                'fake-port')
        response = self.post(url)
        self.assertEqual(202, response.status_int)
        mock_detach.assert_called_once_with(mock.ANY, test_container_obj,
                                            network_uuid)

    @patch('zun.objects.Container.get_by_uuid')
    def test_network_list(self, mock_container_get_by_uuid):
        test_container = utils.get_test_container()
        test_container_obj = objects.Container(self.context, **test_container)
        mock_container_get_by_uuid.return_value = test_container_obj
        container_uuid = test_container.get('uuid')
        url = '/v1/containers/%s/%s' % (container_uuid, 'network_list')
        response = self.get(url)
        self.assertEqual(200, response.status_int)
        mock_container_get_by_uuid.assert_called_once_with(
            mock.ANY,
            test_container['uuid'])
        self._assert_networks(test_container['addresses'],
                              response.json['networks'])

    def _assert_networks(self, addresses, networks):
        self.assertEqual(len(addresses), len(networks))
        for network in networks:
            address_list = addresses[network['net_id']]
            self.assertEqual(len(address_list), len(network['fixed_ips']))
            for address in address_list:
                matched = 0
                for fixed_ip in network['fixed_ips']:
                    if (address['addr'] == fixed_ip['ip_address'] and
                            address['version'] == fixed_ip['version'] and
                            address['subnet_id'] == fixed_ip['subnet_id'] and
                            address['port'] == network['port_id']):
                        matched += 1
                self.assertEqual(1, matched)

    @mock.patch('zun.compute.api.API.remove_security_group')
    @mock.patch('zun.network.neutron.NeutronAPI.find_resourceid_by_name_or_id')
    @mock.patch('zun.api.utils.get_resource')
    def test_remove_security_group_by_uuid(self, mock_get_resource,
                                           mock_find_resourceid,
                                           mock_remove_security_group):
        headers = {"OpenStack-API-Version": "container 1.14"}
        test_container = utils.get_test_container(
            security_groups=['affb9021-964d-4b1b-80a8-9b9db60497e4'])
        test_container_obj = objects.Container(self.context, **test_container)
        mock_get_resource.return_value = test_container_obj
        mock_find_resourceid.return_value = \
            test_container_obj.security_groups[0]
        container_name = test_container.get('name')
        security_group_id_to_remove = test_container_obj.security_groups[0]
        url = '/v1/containers/%s/%s?name=%s' % (container_name,
                                                'remove_security_group',
                                                security_group_id_to_remove)
        response = self.post(url, headers=headers)
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)
        mock_find_resourceid.assert_called_once_with(
            'security_group', security_group_id_to_remove, mock.ANY)
        mock_remove_security_group.assert_called_once_with(
            mock.ANY, test_container_obj,
            test_container_obj.security_groups[0])


class TestContainerEnforcement(api_base.FunctionalTest):

    def _common_policy_check(self, rule, func, *arg, **kwarg):
        rules = dict({rule: 'project_id:non_fake'},
                     **kwarg.pop('bypass_rules', {}))
        self.policy.set_rules(rules)
        response = func(*arg, **kwarg)
        self.assertEqual(403, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(
            "Policy doesn't allow %s to be performed." % rule,
            response.json['errors'][0]['detail'])

    def test_policy_disallow_get_all(self):
        self._common_policy_check(
            'container:get_all', self.get, '/v1/containers/',
            expect_errors=True)

    def test_policy_disallow_get_all_all_projects(self):
        self._common_policy_check(
            'container:get_all_all_projects',
            self.get, '/v1/containers/?all_projects=1',
            expect_errors=True,
            bypass_rules={'container:get_all': 'project_id:fake_project'})

    def test_policy_disallow_get_one(self):
        container = obj_utils.create_test_container(self.context)
        self._common_policy_check(
            'container:get_one', self.get,
            '/v1/containers/%s/' % container.uuid,
            expect_errors=True)

    def test_policy_disallow_get_one_all_projects(self):
        container = obj_utils.create_test_container(self.context)
        self._common_policy_check(
            'container:get_one_all_projects', self.get,
            '/v1/containers/%s/?all_projects=1' % container.uuid,
            expect_errors=True)

    def test_policy_disallow_update(self):
        container = obj_utils.create_test_container(self.context)
        params = {'cpu': 1}
        self._common_policy_check(
            'container:update', self.patch_json,
            '/containers/%s/' % container.uuid, params,
            expect_errors=True)

    def test_policy_disallow_create(self):
        params = ('{"name": "MyDocker", "image": "ubuntu",'
                  '"command": ["env"], "memory": "512"}')

        self._common_policy_check(
            'container:create', self.post, '/v1/containers/',
            params=params,
            content_type='application/json',
            expect_errors=True)

    def test_policy_disallow_delete(self):
        container = obj_utils.create_test_container(self.context)
        self._common_policy_check(
            'container:delete', self.delete,
            '/v1/containers/%s/' % container.uuid,
            expect_errors=True)

    def test_policy_disallow_delete_all_projects(self):
        container = obj_utils.create_test_container(self.context)
        self._common_policy_check(
            'container:delete_all_projects', self.delete,
            '/v1/containers/%s/?all_projects=1' % container.uuid,
            expect_errors=True)

    def test_policy_disallow_delete_force(self):
        container = obj_utils.create_test_container(self.context)
        self._common_policy_check(
            'container:delete_force', self.delete,
            '/v1/containers/%s/?force=True' % container.uuid,
            expect_errors=True,
            bypass_rules={'container:delete': 'project_id:fake_project'})

    def _owner_check(self, rule, func, *args, **kwargs):
        self.policy.set_rules({rule: "user_id:%(user_id)s"})
        response = func(*args, **kwargs)
        self.assertEqual(403, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertTrue(
            "Policy doesn't allow %s to be performed." % rule,
            response.json['errors'][0]['detail'])

    def test_policy_only_owner_get_one(self):
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        self._owner_check("container:get_one", self.get_json,
                          '/containers/%s/' % container.uuid,
                          expect_errors=True)

    def test_policy_only_owner_update(self):
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        self._owner_check(
            "container:update", self.patch_json,
            '/containers/%s/' % container.uuid,
            {'cpu': 1},
            expect_errors=True)

    def test_policy_only_owner_delete(self):
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        self._owner_check(
            "container:delete", self.delete,
            '/containers/%s/' % container.uuid,
            expect_errors=True)

    def test_policy_only_owner_logs(self):
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        self._owner_check("container:logs", self.get_json,
                          '/containers/%s/logs/' % container.uuid,
                          expect_errors=True)

    def test_policy_only_owner_execute(self):
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        self._owner_check("container:execute", self.post_json,
                          '/containers/%s/execute/' % container.uuid,
                          params={'command': 'ls'}, expect_errors=True)

    def test_policy_only_owner_actions(self):
        actions = ['start', 'stop', 'reboot', 'pause', 'unpause']
        container = obj_utils.create_test_container(self.context,
                                                    user_id='another')
        for action in actions:
            self._owner_check('container:%s' % action, self.post_json,
                              '/containers/%s/%s/' % (container.uuid, action),
                              {}, expect_errors=True)


class TestContainerActionController(api_base.FunctionalTest):

    def _format_action(self, action, expect_traceback=True):
        '''Remove keys that aren't serialized.'''
        to_delete = ('id', 'finish_time', 'created_at', 'updated_at',
                     'deleted_at', 'deleted')
        for key in to_delete:
            if key in action:
                del (action[key])
        for event in action.get('events', []):
            self._format_event(event, expect_traceback)
        return action

    def _format_event(self, event, expect_traceback=True):
        '''Remove keys that aren't serialized.'''
        to_delete = ['id', 'created_at', 'updated_at', 'deleted_at', 'deleted',
                     'action_id']
        if not expect_traceback:
            event['traceback'] = None
        for key in to_delete:
            if key in event:
                del (event[key])
        return event

    @mock.patch('zun.objects.Container.get_by_uuid')
    @mock.patch('zun.objects.ContainerAction.get_by_container_uuid')
    def test_list_actions(self, mock_get_by_container_uuid,
                          mock_container_get_by_uuid):
        test_container = utils.get_test_container()
        test_action = utils.get_test_action_value(
            container_uuid=test_container['uuid'])

        container_object = objects.Container(self.context, **test_container)
        action_object = objects.ContainerAction(self.context, **test_action)

        mock_container_get_by_uuid.return_value = container_object
        mock_get_by_container_uuid.return_value = [action_object]
        response = self.get('/v1/containers/%s/container_actions' %
                            test_container['uuid'])

        mock_get_by_container_uuid.assert_called_once_with(
            mock.ANY,
            test_container['uuid'])

        self.assertEqual(200, response.status_int)
        self.assertEqual(
            self._format_action(test_action),
            self._format_action(response.json['containerActions'][0]))

    @mock.patch('zun.objects.Container.get_by_uuid')
    @mock.patch('zun.common.policy.enforce')
    @mock.patch('zun.objects.ContainerActionEvent.get_by_action')
    @mock.patch('zun.objects.ContainerAction.get_by_request_id')
    def test_get_action_with_events_allowed(self, mock_get_by_request_id,
                                            mock_get_by_action, mock_policy,
                                            mock_container_get_by_uuid):
        mock_policy.return_value = True
        test_container = utils.get_test_container()
        test_action = utils.get_test_action_value(
            container_uuid=test_container['uuid'])
        test_event = utils.get_test_action_event_value(
            action_id=test_action['id'])
        test_action['events'] = [test_event]

        container_object = objects.Container(self.context, **test_container)
        action_object = objects.ContainerAction(self.context, **test_action)
        event_object = objects.ContainerActionEvent(self.context, **test_event)

        mock_container_get_by_uuid.return_value = container_object
        mock_get_by_request_id.return_value = action_object
        mock_get_by_action.return_value = [event_object]

        response = self.get('/v1/containers/%s/container_actions/%s' % (
            test_container['uuid'], test_action['request_id']))

        mock_get_by_request_id.assert_called_once_with(
            mock.ANY, test_container['uuid'], test_action['request_id'])
        mock_get_by_action.assert_called_once_with(mock.ANY, test_action['id'])

        self.assertEqual(200, response.status_int)
        self.assertEqual(self._format_action(test_action),
                         self._format_action(response.json))

    @mock.patch('zun.objects.Container.get_by_uuid')
    @mock.patch('zun.common.policy.enforce')
    @mock.patch('zun.objects.ContainerActionEvent.get_by_action')
    @mock.patch('zun.objects.ContainerAction.get_by_request_id')
    def test_get_action_with_events_not_allowed(self, mock_get_by_request_id,
                                                mock_get_by_action,
                                                mock_policy,
                                                mock_container_get_by_uuid):
        mock_policy.return_value = False
        test_container = utils.get_test_container()
        container_obj = objects.Container(self.context, **test_container)
        test_action = utils.get_test_action_value(
            container_uuid=test_container['uuid'])
        test_event = utils.get_test_action_event_value(
            action_id=test_action['id'])
        test_action['events'] = [test_event]
        action_object = objects.ContainerAction(self.context, **test_action)
        event_object = objects.ContainerActionEvent(self.context, **test_event)

        mock_container_get_by_uuid.return_value = container_obj
        mock_get_by_request_id.return_value = action_object
        mock_get_by_action.return_value = [event_object]

        response = self.get('/v1/containers/%s/container_actions/%s' % (
            test_container['uuid'], test_action['request_id']))

        mock_get_by_request_id.assert_called_once_with(
            mock.ANY, test_container['uuid'], test_action['request_id'])
        mock_get_by_action.assert_called_once_with(mock.ANY, test_action['id'])

        self.assertEqual(200, response.status_int)
        self.assertEqual(self._format_action(test_action,
                                             expect_traceback=False),
                         self._format_action(response.json))

    @mock.patch('zun.objects.Container.get_by_uuid')
    @mock.patch('zun.objects.ContainerAction.get_by_request_id')
    def test_action_not_found(self, mock_get_by_request_id,
                              mock_container_get_by_uuid):

        test_container = utils.get_test_container()
        container_obj = objects.Container(self.context, **test_container)

        mock_container_get_by_uuid.return_value = container_obj
        mock_get_by_request_id.return_value = None

        fake_request_id = 'request'

        self.assertRaises(AppError, self.get,
                          ('/v1/containers/%s/container_actions/%s' %
                           (test_container['uuid'], fake_request_id)))
        mock_get_by_request_id.assert_called_once_with(
            mock.ANY, test_container['uuid'], fake_request_id)

    @mock.patch('zun.objects.Container.get_by_uuid')
    def test_container_not_found(self, mock_container_get_by_uuid):
        test_container = utils.get_test_container()

        self.assertRaises(AppError, self.get,
                          ('/v1/containers/%s/container_actions'
                           % test_container['uuid']))
        mock_container_get_by_uuid.assert_called_once_with(
            mock.ANY, test_container['uuid'])


class TestContainerActionEnforcement(api_base.FunctionalTest):

    def _common_policy_check(self, rule, func, *arg, **kwarg):
        rules = dict({rule: 'project_id:non_fake'},
                     **kwarg.pop('bypass_rules', {}))
        self.policy.set_rules(rules)
        response = func(*arg, **kwarg)
        self.assertEqual(403, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(
            "Policy doesn't allow %s to be performed." % rule,
            response.json['errors'][0]['detail'])

    def test_list_actions_disallow_by_project(self):
        container = obj_utils.create_test_container(self.context)

        self._common_policy_check(
            'container:actions', self.get,
            '/v1/containers/%s/container_actions/' % container.uuid,
            expect_errors=True)

    def test_get_action_disallow_by_project(self):
        container = obj_utils.create_test_container(self.context)

        self._common_policy_check(
            'container:actions', self.get,
            '/v1/containers/%s/container_actions/fake_request' %
            container.uuid, expect_errors=True)
