# Copyright 2011 OpenStack Foundation
# 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.

"""Tests the Swift backend store"""

import copy
import fixtures
import hashlib
import httplib
import mock
import tempfile
import uuid

from oslo_config import cfg
from oslo_utils import units
from oslotest import moxstubout
import requests_mock
import six
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
from six.moves import range
import StringIO
import swiftclient

from glance_store._drivers.swift import store as swift
from glance_store import backend
from glance_store import BackendException
from glance_store import capabilities
from glance_store.common import auth
from glance_store.common import utils
from glance_store import exceptions
from glance_store import location
from glance_store.tests import base
from tests.unit import test_store_capabilities


CONF = cfg.CONF

FAKE_UUID = lambda: str(uuid.uuid4())
FAKE_UUID2 = lambda: str(uuid.uuid4())

Store = swift.Store
FIVE_KB = 5 * units.Ki
FIVE_GB = 5 * units.Gi
MAX_SWIFT_OBJECT_SIZE = FIVE_GB
SWIFT_PUT_OBJECT_CALLS = 0
SWIFT_CONF = {'swift_store_auth_address': 'localhost:8080',
              'swift_store_container': 'glance',
              'swift_store_user': 'user',
              'swift_store_key': 'key',
              'swift_store_auth_address': 'localhost:8080',
              'swift_store_container': 'glance',
              'swift_store_retry_get_count': 1,
              'default_swift_reference': 'ref1'
              }


# We stub out as little as possible to ensure that the code paths
# between swift and swiftclient are tested
# thoroughly
def stub_out_swiftclient(stubs, swift_store_auth_version):
    fixture_containers = ['glance']
    fixture_container_headers = {}
    fixture_headers = {
        'glance/%s' % FAKE_UUID: {
            'content-length': FIVE_KB,
            'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'
        },
        'glance/%s' % FAKE_UUID2: {'x-static-large-object': 'true', },
    }
    fixture_objects = {'glance/%s' % FAKE_UUID: six.StringIO("*" * FIVE_KB),
                       'glance/%s' % FAKE_UUID2: six.StringIO("*" * FIVE_KB), }

    def fake_head_container(url, token, container, **kwargs):
        if container not in fixture_containers:
            msg = "No container %s found" % container
            raise swiftclient.ClientException(msg,
                                              http_status=httplib.NOT_FOUND)
        return fixture_container_headers

    def fake_put_container(url, token, container, **kwargs):
        fixture_containers.append(container)

    def fake_post_container(url, token, container, headers, http_conn=None):
        for key, value in six.iteritems(headers):
            fixture_container_headers[key] = value

    def fake_put_object(url, token, container, name, contents, **kwargs):
        # PUT returns the ETag header for the newly-added object
        # Large object manifest...
        global SWIFT_PUT_OBJECT_CALLS
        SWIFT_PUT_OBJECT_CALLS += 1
        CHUNKSIZE = 64 * units.Ki
        fixture_key = "%s/%s" % (container, name)
        if fixture_key not in fixture_headers:
            if kwargs.get('headers'):
                etag = kwargs['headers']['ETag']
                manifest = kwargs.get('headers').get('X-Object-Manifest')
                fixture_headers[fixture_key] = {'manifest': True,
                                                'etag': etag,
                                                'x-object-manifest': manifest}
                fixture_objects[fixture_key] = None
                return etag
            if hasattr(contents, 'read'):
                fixture_object = six.StringIO()
                chunk = contents.read(CHUNKSIZE)
                checksum = hashlib.md5()
                while chunk:
                    fixture_object.write(chunk)
                    checksum.update(chunk)
                    chunk = contents.read(CHUNKSIZE)
                etag = checksum.hexdigest()
            else:
                fixture_object = six.StringIO(contents)
                etag = hashlib.md5(fixture_object.getvalue()).hexdigest()
            read_len = fixture_object.len
            if read_len > MAX_SWIFT_OBJECT_SIZE:
                msg = ('Image size:%d exceeds Swift max:%d' %