# Copyright (c) 2018 European Organization for Nuclear Research.
# All Rights Reserved.
#
#    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 copy

import testtools
from testtools import matchers

from magnumclient import exceptions
from magnumclient.tests import utils
from magnumclient.v1 import nodegroups


NODEGROUP1 = {
    'id': 123,
    'uuid': '66666666-7777-8888-9999-000000000001',
    'cluster_id': '66666666-7777-8888-9999-000000000000',
    'name': 'test-worker',
    'node_addresses': ['172.17.2.3'],
    'node_count': 2,
    'project_id': 'fake_project',
    'labels': {},
    'flavor_id': 'fake_flavor_1',
    'image_id': 'fake_image',
    'is_default': True,
    'role': 'worker',
    'max_node_count': 10,
    'min_node_count': 0
}
NODEGROUP2 = {
    'id': 124,
    'uuid': '66666666-7777-8888-9999-000000000002',
    'cluster_id': '66666666-7777-8888-9999-000000000000',
    'name': 'test-master',
    'node_addresses': ['172.17.2.4'],
    'node_count': 2,
    'project_id': 'fake_project',
    'labels': {},
    'flavor_id': 'fake_flavor_1',
    'image_id': 'fake_image',
    'is_default': True,
    'role': 'master',
    'max_node_count': 10,
    'min_node_count': 0
}

CREATE_NODEGROUP = copy.deepcopy(NODEGROUP1)
del CREATE_NODEGROUP['id']
del CREATE_NODEGROUP['uuid']
del CREATE_NODEGROUP['node_addresses']
del CREATE_NODEGROUP['is_default']
del CREATE_NODEGROUP['cluster_id']

UPDATED_NODEGROUP = copy.deepcopy(NODEGROUP1)
NEW_NODE_COUNT = 9
UPDATED_NODEGROUP['node_count'] = NEW_NODE_COUNT


fake_responses = {
    '/v1/clusters/test/nodegroups/':
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
        'POST': (
            {},
            CREATE_NODEGROUP,
        ),
    },
    '/v1/clusters/test/nodegroups/%s' % NODEGROUP1['id']:
    {
        'GET': (
            {},
            NODEGROUP1
        ),
        'DELETE': (
            {},
            None,
        ),
        'PATCH': (
            {},
            UPDATED_NODEGROUP,
        ),
    },
    '/v1/clusters/test/nodegroups/%s/?rollback=True' % NODEGROUP1['id']:
        {
            'PATCH': (
                {},
                UPDATED_NODEGROUP,
            ),
        },
    '/v1/clusters/test/nodegroups/%s' % NODEGROUP1['name']:
    {
        'GET': (
            {},
            NODEGROUP1
        ),
        'DELETE': (
            {},
            None,
        ),
        'PATCH': (
            {},
            UPDATED_NODEGROUP,
        ),
    },
    '/v1/clusters/test/nodegroups/?limit=2':
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
    },
    '/v1/clusters/test/nodegroups/?marker=%s' % NODEGROUP2['uuid']:
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
    },
    '/v1/clusters/test/nodegroups/?limit=2&marker=%s' % NODEGROUP2['uuid']:
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
    },
    '/v1/clusters/test/nodegroups/?sort_dir=asc':
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
    },
    '/v1/clusters/test/nodegroups/?sort_key=uuid':
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP1, NODEGROUP2]},
        ),
    },
    '/v1/clusters/test/nodegroups/?sort_key=uuid&sort_dir=desc':
    {
        'GET': (
            {},
            {'nodegroups': [NODEGROUP2, NODEGROUP1]},
        ),
    },
}


class NodeGroupManagerTest(testtools.TestCase):

    def setUp(self):
        super(NodeGroupManagerTest, self).setUp()
        self.api = utils.FakeAPI(fake_responses)
        self.mgr = nodegroups.NodeGroupManager(self.api)
        self.cluster_id = 'test'
        self.base_path = '/v1/clusters/test/nodegroups/'

    def test_nodegroup_list(self):
        clusters = self.mgr.list(self.cluster_id)
        expect = [
            ('GET', self.base_path, {}, None),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertThat(clusters, matchers.HasLength(2))

    def _test_nodegroup_list_with_filters(self, cluster_id, limit=None,
                                          marker=None, sort_key=None,
                                          sort_dir=None, detail=False,
                                          expect=[]):
        nodegroup_filter = self.mgr.list(cluster_id,
                                         limit=limit,
                                         marker=marker,
                                         sort_key=sort_key,
                                         sort_dir=sort_dir,
                                         detail=detail)
        self.assertEqual(expect, self.api.calls)
        self.assertThat(nodegroup_filter, matchers.HasLength(2))

    def test_nodegroup_list_with_limit(self):
        expect = [
            ('GET', self.base_path + '?limit=2', {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            limit=2,
            expect=expect)

    def test_nodegroup_list_with_marker(self):
        filter_ = '?marker=%s' % NODEGROUP2['uuid']
        expect = [
            ('GET', self.base_path + filter_, {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            marker=NODEGROUP2['uuid'],
            expect=expect)

    def test_nodegroup_list_with_marker_limit(self):
        filter_ = '?limit=2&marker=%s' % NODEGROUP2['uuid']
        expect = [
            ('GET', self.base_path + filter_, {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            limit=2, marker=NODEGROUP2['uuid'],
            expect=expect)

    def test_nodegroup_list_with_sort_dir(self):
        expect = [
            ('GET', '/v1/clusters/test/nodegroups/?sort_dir=asc', {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            sort_dir='asc',
            expect=expect)

    def test_nodegroup_list_with_sort_key(self):
        expect = [
            ('GET', '/v1/clusters/test/nodegroups/?sort_key=uuid', {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            sort_key='uuid',
            expect=expect)

    def test_nodegroup_list_with_sort_key_dir(self):
        expect = [
            ('GET', self.base_path + '?sort_key=uuid&sort_dir=desc', {}, None),
        ]
        self._test_nodegroup_list_with_filters(
            self.cluster_id,
            sort_key='uuid', sort_dir='desc',
            expect=expect)

    def test_nodegroup_show_by_name(self):
        nodegroup = self.mgr.get(self.cluster_id, NODEGROUP1['name'])
        expect = [
            ('GET', self.base_path + '%s' % NODEGROUP1['name'], {}, None)
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertEqual(NODEGROUP1['name'], nodegroup.name)

    def test_nodegroup_show_by_id(self):
        nodegroup = self.mgr.get(self.cluster_id, NODEGROUP1['id'])
        expect = [
            ('GET', self.base_path + '%s' % NODEGROUP1['id'], {}, None)
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertEqual(NODEGROUP1['name'], nodegroup.name)

    def test_nodegroup_delete_by_id(self):
        nodegroup = self.mgr.delete(self.cluster_id, NODEGROUP1['id'])
        expect = [
            ('DELETE', self.base_path + '%s' % NODEGROUP1['id'], {}, None),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertIsNone(nodegroup)

    def test_nodegroup_delete_by_name(self):
        nodegroup = self.mgr.delete(self.cluster_id, NODEGROUP1['name'])
        expect = [
            ('DELETE', self.base_path + '%s' % NODEGROUP1['name'], {}, None),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertIsNone(nodegroup)

    def test_nodegroup_update(self):
        patch = {'op': 'replace',
                 'value': NEW_NODE_COUNT,
                 'path': '/node_count'}
        nodegroup = self.mgr.update(self.cluster_id, id=NODEGROUP1['id'],
                                    patch=patch)
        expect = [
            ('PATCH', self.base_path + '%s' % NODEGROUP1['id'], {}, patch),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertEqual(NEW_NODE_COUNT, nodegroup.node_count)

    def test_nodegroup_create(self):
        nodegroup = self.mgr.create(self.cluster_id, **CREATE_NODEGROUP)
        expect = [
            ('POST', self.base_path, {}, CREATE_NODEGROUP),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertTrue(nodegroup)

    def test_nodegroup_create_with_docker_volume_size(self):
        ng_with_volume_size = dict()
        ng_with_volume_size.update(CREATE_NODEGROUP)
        ng_with_volume_size['docker_volume_size'] = 20
        nodegroup = self.mgr.create(self.cluster_id, **ng_with_volume_size)
        expect = [
            ('POST', self.base_path, {}, ng_with_volume_size),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertTrue(nodegroup)

    def test_nodegroup_create_with_labels(self):
        ng_with_labels = dict()
        ng_with_labels.update(CREATE_NODEGROUP)
        ng_with_labels['labels'] = "key=val"
        nodegroup = self.mgr.create(self.cluster_id, **ng_with_labels)
        expect = [
            ('POST', self.base_path, {}, ng_with_labels),
        ]
        self.assertEqual(expect, self.api.calls)
        self.assertTrue(nodegroup)

    def test_nodegroup_create_fail(self):
        CREATE_NODEGROUP_FAIL = copy.deepcopy(CREATE_NODEGROUP)
        CREATE_NODEGROUP_FAIL["wrong_key"] = "wrong"
        self.assertRaisesRegex(exceptions.InvalidAttribute,
                               ("Key must be in %s" %
                                ','.join(nodegroups.CREATION_ATTRIBUTES)),
                               self.mgr.create, self.cluster_id,
                               **CREATE_NODEGROUP_FAIL)
        self.assertEqual([], self.api.calls)
