# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
#
# 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.

"""
test_cloud_endpoints
----------------------------------

Tests Keystone endpoints commands.
"""

import uuid

from openstack.cloud.exc import OpenStackCloudException
from openstack.cloud.exc import OpenStackCloudUnavailableFeature
from openstack.tests.unit import base
from testtools import matchers


class TestCloudEndpoints(base.TestCase):

    def get_mock_url(self, service_type='identity', interface='public',
                     resource='endpoints', append=None, base_url_append='v3'):
        return super(TestCloudEndpoints, self).get_mock_url(
            service_type, interface, resource, append, base_url_append)

    def _dummy_url(self):
        return 'https://%s.example.com/' % uuid.uuid4().hex

    def test_create_endpoint_v2(self):
        self.use_keystone_v2()
        service_data = self._get_service_data()
        endpoint_data = self._get_endpoint_v2_data(
            service_data.service_id, public_url=self._dummy_url(),
            internal_url=self._dummy_url(), admin_url=self._dummy_url())
        other_endpoint_data = self._get_endpoint_v2_data(
            service_data.service_id, region=endpoint_data.region,
            public_url=endpoint_data.public_url)
        # correct the keys

        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='services', base_url_append='OS-KSADM'),
                 status_code=200,
                 json={'OS-KSADM:services': [
                     service_data.json_response_v2['OS-KSADM:service']]}),
            dict(method='POST',
                 uri=self.get_mock_url(base_url_append=None),
                 status_code=200,
                 json=endpoint_data.json_response,
                 validate=dict(json=endpoint_data.json_request)),
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='services', base_url_append='OS-KSADM'),
                 status_code=200,
                 json={'OS-KSADM:services': [
                     service_data.json_response_v2['OS-KSADM:service']]}),
            # NOTE(notmorgan): There is a stupid happening here, we do two
            # gets on the services for some insane reason (read: keystoneclient
            # is bad and should feel bad).
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='services', base_url_append='OS-KSADM'),
                 status_code=200,
                 json={'OS-KSADM:services': [
                     service_data.json_response_v2['OS-KSADM:service']]}),
            dict(method='POST',
                 uri=self.get_mock_url(base_url_append=None),
                 status_code=200,
                 json=other_endpoint_data.json_response,
                 validate=dict(json=other_endpoint_data.json_request))
        ])

        endpoints = self.cloud.create_endpoint(
            service_name_or_id=service_data.service_id,
            region=endpoint_data.region,
            public_url=endpoint_data.public_url,
            internal_url=endpoint_data.internal_url,
            admin_url=endpoint_data.admin_url
        )

        self.assertThat(endpoints[0].id,
                        matchers.Equals(endpoint_data.endpoint_id))
        self.assertThat(endpoints[0].region,
                        matchers.Equals(endpoint_data.region))
        self.assertThat(endpoints[0].publicURL,
                        matchers.Equals(endpoint_data.public_url))
        self.assertThat(endpoints[0].internalURL,
                        matchers.Equals(endpoint_data.internal_url))
        self.assertThat(endpoints[0].adminURL,
                        matchers.Equals(endpoint_data.admin_url))

        # test v3 semantics on v2.0 endpoint
        self.assertRaises(OpenStackCloudException,
                          self.cloud.create_endpoint,
                          service_name_or_id='service1',
                          interface='mock_admin_url',
                          url='admin')

        endpoints_3on2 = self.cloud.create_endpoint(
            service_name_or_id=service_data.service_id,
            region=endpoint_data.region,
            interface='public',
            url=endpoint_data.public_url
        )

        # test keys and values are correct
        self.assertThat(
            endpoints_3on2[0].region,
            matchers.Equals(other_endpoint_data.region))
        self.assertThat(
            endpoints_3on2[0].publicURL,
            matchers.Equals(other_endpoint_data.public_url))
        self.assertThat(endpoints_3on2[0].get('internalURL'),
                        matchers.Equals(None))
        self.assertThat(endpoints_3on2[0].get('adminURL'),
                        matchers.Equals(None))
        self.assert_calls()

    def test_create_endpoint_v3(self):
        service_data = self._get_service_data()
        public_endpoint_data = self._get_endpoint_v3_data(
            service_id=service_data.service_id, interface='public',
            url=self._dummy_url())
        public_endpoint_data_disabled = self._get_endpoint_v3_data(
            service_id=service_data.service_id, interface='public',
            url=self._dummy_url(), enabled=False)
        admin_endpoint_data = self._get_endpoint_v3_data(
            service_id=service_data.service_id, interface='admin',
            url=self._dummy_url(), region=public_endpoint_data.region)
        internal_endpoint_data = self._get_endpoint_v3_data(
            service_id=service_data.service_id, interface='internal',
            url=self._dummy_url(), region=public_endpoint_data.region)

        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(resource='services'),
                 status_code=200,
                 json={'services': [
                     service_data.json_response_v3['service']]}),
            dict(method='POST',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json=public_endpoint_data_disabled.json_response,
                 validate=dict(
                     json=public_endpoint_data_disabled.json_request)),
            dict(method='GET',
                 uri=self.get_mock_url(resource='services'),
                 status_code=200,
                 json={'services': [
                     service_data.json_response_v3['service']]}),
            dict(method='POST',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json=public_endpoint_data.json_response,
                 validate=dict(json=public_endpoint_data.json_request)),
            dict(method='POST',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json=internal_endpoint_data.json_response,
                 validate=dict(json=internal_endpoint_data.json_request)),
            dict(method='POST',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json=admin_endpoint_data.json_response,
                 validate=dict(json=admin_endpoint_data.json_request)),
        ])

        endpoints = self.cloud.create_endpoint(
            service_name_or_id=service_data.service_id,
            region=public_endpoint_data_disabled.region,
            url=public_endpoint_data_disabled.url,
            interface=public_endpoint_data_disabled.interface,
            enabled=False)

        # Test endpoint values
        self.assertThat(
            endpoints[0].id,
            matchers.Equals(public_endpoint_data_disabled.endpoint_id))
        self.assertThat(endpoints[0].url,
                        matchers.Equals(public_endpoint_data_disabled.url))
        self.assertThat(
            endpoints[0].interface,
            matchers.Equals(public_endpoint_data_disabled.interface))
        self.assertThat(
            endpoints[0].region,
            matchers.Equals(public_endpoint_data_disabled.region))
        self.assertThat(
            endpoints[0].region_id,
            matchers.Equals(public_endpoint_data_disabled.region))
        self.assertThat(endpoints[0].enabled,
                        matchers.Equals(public_endpoint_data_disabled.enabled))

        endpoints_2on3 = self.cloud.create_endpoint(
            service_name_or_id=service_data.service_id,
            region=public_endpoint_data.region,
            public_url=public_endpoint_data.url,
            internal_url=internal_endpoint_data.url,
            admin_url=admin_endpoint_data.url)

        # Three endpoints should be returned, public, internal, and admin
        self.assertThat(len(endpoints_2on3), matchers.Equals(3))

        # test keys and values are correct for each endpoint created
        for result, reference in zip(
                endpoints_2on3, [public_endpoint_data,
                                 internal_endpoint_data,
                                 admin_endpoint_data]
        ):
            self.assertThat(result.id, matchers.Equals(reference.endpoint_id))
            self.assertThat(result.url, matchers.Equals(reference.url))
            self.assertThat(result.interface,
                            matchers.Equals(reference.interface))
            self.assertThat(result.region,
                            matchers.Equals(reference.region))
            self.assertThat(result.enabled, matchers.Equals(reference.enabled))
        self.assert_calls()

    def test_update_endpoint_v2(self):
        self.use_keystone_v2()
        self.assertRaises(OpenStackCloudUnavailableFeature,
                          self.cloud.update_endpoint, 'endpoint_id')

    def test_update_endpoint_v3(self):
        service_data = self._get_service_data()
        dummy_url = self._dummy_url()
        endpoint_data = self._get_endpoint_v3_data(
            service_id=service_data.service_id, interface='admin',
            enabled=False)
        reference_request = endpoint_data.json_request.copy()
        reference_request['endpoint']['url'] = dummy_url
        self.register_uris([
            dict(method='PATCH',
                 uri=self.get_mock_url(append=[endpoint_data.endpoint_id]),
                 status_code=200,
                 json=endpoint_data.json_response,
                 validate=dict(json=reference_request))
        ])
        endpoint = self.cloud.update_endpoint(
            endpoint_data.endpoint_id,
            service_name_or_id=service_data.service_id,
            region=endpoint_data.region,
            url=dummy_url,
            interface=endpoint_data.interface,
            enabled=False
        )

        # test keys and values are correct
        self.assertThat(endpoint.id,
                        matchers.Equals(endpoint_data.endpoint_id))
        self.assertThat(endpoint.service_id,
                        matchers.Equals(service_data.service_id))
        self.assertThat(endpoint.url,
                        matchers.Equals(endpoint_data.url))
        self.assertThat(endpoint.interface,
                        matchers.Equals(endpoint_data.interface))

        self.assert_calls()

    def test_list_endpoints(self):
        endpoints_data = [self._get_endpoint_v3_data() for e in range(1, 10)]
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [e.json_response['endpoint']
                                     for e in endpoints_data]})
        ])

        endpoints = self.cloud.list_endpoints()
        # test we are getting exactly len(self.mock_endpoints) elements
        self.assertThat(len(endpoints), matchers.Equals(len(endpoints_data)))

        # test keys and values are correct
        for i, ep in enumerate(endpoints_data):
            self.assertThat(endpoints[i].id,
                            matchers.Equals(ep.endpoint_id))
            self.assertThat(endpoints[i].service_id,
                            matchers.Equals(ep.service_id))
            self.assertThat(endpoints[i].url,
                            matchers.Equals(ep.url))
            self.assertThat(endpoints[i].interface,
                            matchers.Equals(ep.interface))

        self.assert_calls()

    def test_search_endpoints(self):
        endpoints_data = [self._get_endpoint_v3_data(region='region1')
                          for e in range(0, 2)]
        endpoints_data.extend([self._get_endpoint_v3_data()
                               for e in range(1, 8)])
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [e.json_response['endpoint']
                                     for e in endpoints_data]}),
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [e.json_response['endpoint']
                                     for e in endpoints_data]}),
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [e.json_response['endpoint']
                                     for e in endpoints_data]}),
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [e.json_response['endpoint']
                                     for e in endpoints_data]})
        ])

        # Search by id
        endpoints = self.cloud.search_endpoints(
            id=endpoints_data[-1].endpoint_id)
        # # test we are getting exactly 1 element
        self.assertEqual(1, len(endpoints))
        self.assertThat(endpoints[0].id,
                        matchers.Equals(endpoints_data[-1].endpoint_id))
        self.assertThat(endpoints[0].service_id,
                        matchers.Equals(endpoints_data[-1].service_id))
        self.assertThat(endpoints[0].url,
                        matchers.Equals(endpoints_data[-1].url))
        self.assertThat(endpoints[0].interface,
                        matchers.Equals(endpoints_data[-1].interface))

        # Not found
        endpoints = self.cloud.search_endpoints(id='!invalid!')
        self.assertEqual(0, len(endpoints))

        # Multiple matches
        endpoints = self.cloud.search_endpoints(
            filters={'region_id': 'region1'})
        # # test we are getting exactly 2 elements
        self.assertEqual(2, len(endpoints))

        # test we are getting the correct response for region/region_id compat
        endpoints = self.cloud.search_endpoints(
            filters={'region': 'region1'})
        # # test we are getting exactly 2 elements, this is v3
        self.assertEqual(2, len(endpoints))

        self.assert_calls()

    def test_delete_endpoint(self):
        endpoint_data = self._get_endpoint_v3_data()
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(),
                 status_code=200,
                 json={'endpoints': [
                     endpoint_data.json_response['endpoint']]}),
            dict(method='DELETE',
                 uri=self.get_mock_url(append=[endpoint_data.endpoint_id]),
                 status_code=204)
        ])

        # Delete by id
        self.cloud.delete_endpoint(id=endpoint_data.endpoint_id)
        self.assert_calls()
