# 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.

import fixtures
import mock
from oslo_config import cfg
from webob import exc as webob_exc

try:
    import configparser as ConfigParser
except ImportError:
    import ConfigParser
import shutil
import webtest

from magnum.api import app
from magnum.api.controllers import v1 as v1_api
from magnum.tests import base as test_base
from magnum.tests.unit.api import base as api_base


class TestRootController(api_base.FunctionalTest):
    def setUp(self):
        super(TestRootController, self).setUp()
        self.root_expected = {
            u'description': u'Magnum is an OpenStack project which '
                            'aims to provide container cluster management.',
            u'name': u'OpenStack Magnum API',
            u'versions': [{u'id': u'v1',
                           u'links':
                               [{u'href': u'http://localhost/v1/',
                                 u'rel': u'self'}],
                           u'status': u'CURRENT',
                           u'max_version': u'1.3',
                           u'min_version': u'1.1'}]}

        self.v1_expected = {
            u'media_types':
                [{u'base': u'application/json',
                  u'type': u'application/vnd.openstack.magnum.v1+json'}],
            u'links': [{u'href': u'http://localhost/v1/',
                        u'rel': u'self'},
                       {u'href':
                        u'http://docs.openstack.org/developer'
                        '/magnum/dev/api-spec-v1.html',
                        u'type': u'text/html', u'rel': u'describedby'}],
            u'bays': [{u'href': u'http://localhost/v1/bays/',
                       u'rel': u'self'},
                      {u'href': u'http://localhost/bays/',
                       u'rel': u'bookmark'}],
            u'baymodels': [{u'href': u'http://localhost/v1/baymodels/',
                            u'rel': u'self'},
                           {u'href': u'http://localhost/baymodels/',
                            u'rel': u'bookmark'}],
            u'clusters': [{u'href': u'http://localhost/v1/clusters/',
                           u'rel': u'self'},
                          {u'href': u'http://localhost/clusters/',
                           u'rel': u'bookmark'}],
            u'clustertemplates':
                [{u'href': u'http://localhost/v1/clustertemplates/',
                  u'rel': u'self'},
                 {u'href': u'http://localhost/clustertemplates/',
                  u'rel': u'bookmark'}],
            u'id': u'v1',
            u'certificates': [{u'href': u'http://localhost/v1/certificates/',
                               u'rel': u'self'},
                              {u'href': u'http://localhost/certificates/',
                               u'rel': u'bookmark'}],
            u'mservices': [{u'href': u'http://localhost/v1/mservices/',
                            u'rel': u'self'},
                           {u'href': u'http://localhost/mservices/',
                            u'rel': u'bookmark'}]}

    def make_app(self, paste_file):
        file_name = self.get_path(paste_file)
        cfg.CONF.set_override("api_paste_config", file_name, group="api")
        return webtest.TestApp(app.load_app())

    def test_version(self):
        response = self.app.get('/')
        self.assertEqual(self.root_expected, response.json)

    def test_v1_controller(self):
        response = self.app.get('/v1/')
        self.assertEqual(self.v1_expected, response.json)

    def test_get_not_found(self):
        response = self.app.get('/a/bogus/url', expect_errors=True)
        assert response.status_int == 404

    def test_api_paste_file_not_exist(self):
        cfg.CONF.set_override('api_paste_config', 'non-existent-file',
                              group='api')
        with mock.patch.object(cfg.CONF, 'find_file') as ff:
            ff.return_value = None
            self.assertRaises(cfg.ConfigFilesNotFoundError, app.load_app)

    @mock.patch('magnum.api.app.deploy')
    def test_api_paste_file_not_exist_not_abs(self, mock_deploy):
        path = self.get_path(cfg.CONF['api']['api_paste_config'] + 'test')
        cfg.CONF.set_override('api_paste_config', path, group='api')
        self.assertRaises(cfg.ConfigFilesNotFoundError, app.load_app)

    def test_noauth(self):
        # Don't need to auth
        paste_file = "magnum/tests/unit/api/controllers/noauth-paste.ini"
        app = self.make_app(paste_file)

        response = app.get('/')
        self.assertEqual(self.root_expected, response.json)

        response = app.get('/v1/')
        self.assertEqual(self.v1_expected, response.json)

        response = app.get('/v1/baymodels')
        self.assertEqual(200, response.status_int)

    def test_auth_with_no_public_routes(self):
        # All apis need auth when access
        paste_file = "magnum/tests/unit/api/controllers/auth-paste.ini"
        app = self.make_app(paste_file)

        response = app.get('/', expect_errors=True)
        self.assertEqual(401, response.status_int)

        response = app.get('/v1/', expect_errors=True)
        self.assertEqual(401, response.status_int)

    def test_auth_with_root_access(self):
        # Only / can access without auth
        paste_file = "magnum/tests/unit/api/controllers/auth-root-access.ini"
        app = self.make_app(paste_file)

        response = app.get('/')
        self.assertEqual(self.root_expected, response.json)

        response = app.get('/v1/', expect_errors=True)
        self.assertEqual(401, response.status_int)

        response = app.get('/v1/baymodels', expect_errors=True)
        self.assertEqual(401, response.status_int)

    def test_auth_with_v1_access(self):
        # Only /v1 can access without auth
        paste_file = "magnum/tests/unit/api/controllers/auth-v1-access.ini"
        app = self.make_app(paste_file)

        response = app.get('/', expect_errors=True)
        self.assertEqual(401, response.status_int)

        response = app.get('/v1/')
        self.assertEqual(self.v1_expected, response.json)

        response = app.get('/v1/baymodels', expect_errors=True)
        self.assertEqual(401, response.status_int)


class TestHeathcheck(api_base.FunctionalTest):
    def setUp(self):
        self.addCleanup(self.remove_files)
        super(TestHeathcheck, self).setUp()

        # Create Temporary file
        self.tempdir = self.useFixture(fixtures.TempDir()).path
        paste_ini = "magnum/tests/unit/api/controllers/auth-paste.ini"

        # Read current file and create new one
        config = ConfigParser.RawConfigParser()
        config.read(self.get_path(paste_ini))
        config.set('filter:healthcheck',
                   'disable_by_file_path',
                   self.tempdir + "/disable")
        with open(self.tempdir + "/paste.ini", 'wt') as configfile:
            config.write(configfile)

        # Set config and create app
        cfg.CONF.set_override("api_paste_config",
                              self.tempdir + "/paste.ini",
                              group="api")
        self.app = webtest.TestApp(app.load_app())

    def remove_files(self):
        shutil.rmtree(self.tempdir, ignore_errors=True)

    def test_healthcheck_enabled(self):
        # Check the healthcheck works
        response = self.app.get('/healthcheck')
        self.assertEqual(200, response.status_int)
        self.assertEqual(b"OK", response.body)

    def test_healthcheck_disable_file(self):
        # Create the file that disables healthcheck
        fo = open(self.tempdir + "/disable", 'a')
        fo.close()

        response = self.app.get('/healthcheck', expect_errors=True)
        self.assertEqual(503, response.status_int)
        self.assertEqual(b"DISABLED BY FILE", response.body)


class TestV1Routing(api_base.FunctionalTest):
    def test_route_checks_version(self):
        self.get_json('/')
        self._check_version.assert_called_once_with(mock.ANY,
                                                    mock.ANY)


class TestCheckVersions(test_base.TestCase):
    def setUp(self):
        super(TestCheckVersions, self).setUp()

        class ver(object):
            major = None
            minor = None

        self.version = ver()

    def test_check_version_invalid_major_version(self):
        self.version.major = v1_api.BASE_VERSION + 1
        self.version.minor = v1_api.MIN_VER.minor
        self.assertRaises(webob_exc.HTTPNotAcceptable,
                          v1_api.Controller()._check_version,
                          self.version)

    def test_check_version_too_low(self):
        self.version.major = v1_api.BASE_VERSION
        self.version.minor = v1_api.MIN_VER.minor - 1
        self.assertRaises(webob_exc.HTTPNotAcceptable,
                          v1_api.Controller()._check_version,
                          self.version)

    def test_check_version_too_high(self):
        self.version.major = v1_api.BASE_VERSION
        self.version.minor = v1_api.MAX_VER.minor + 1
        e = self.assertRaises(webob_exc.HTTPNotAcceptable,
                              v1_api.Controller()._check_version,
                              self.version, {'fake-headers':
                                             v1_api.MAX_VER.minor})

        self.assertEqual(v1_api.MAX_VER.minor, e.headers['fake-headers'])

    def test_check_version_ok(self):
        self.version.major = v1_api.BASE_VERSION
        self.version.minor = v1_api.MIN_VER.minor
        v1_api.Controller()._check_version(self.version)
