2012-05-08 15:51:15 +01:00
|
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
|
2017-03-07 13:38:22 +04:00
|
|
|
import gzip
|
2017-03-07 16:52:51 -08:00
|
|
|
import json
|
2013-10-15 09:46:46 -07:00
|
|
|
import logging
|
2022-02-17 11:37:13 +00:00
|
|
|
import io
|
2012-05-08 15:51:15 +01:00
|
|
|
import socket
|
2015-09-23 10:42:43 -07:00
|
|
|
import string
|
2015-12-04 11:28:05 -08:00
|
|
|
import unittest
|
2022-05-24 11:56:35 +10:00
|
|
|
from unittest import mock
|
2013-03-05 16:56:02 +00:00
|
|
|
import warnings
|
2014-10-29 10:11:29 +00:00
|
|
|
import tempfile
|
|
|
|
from hashlib import md5
|
2022-02-17 11:37:13 +00:00
|
|
|
from urllib.parse import urlparse
|
2023-03-17 16:14:15 -07:00
|
|
|
from requests.exceptions import RequestException, SSLError
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-04-28 16:56:44 +01:00
|
|
|
from .utils import (MockHttpTest, fake_get_auth_keystone, StubResponse,
|
2018-09-07 16:50:10 -07:00
|
|
|
FakeKeystone)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-03-04 14:01:55 +00:00
|
|
|
from swiftclient.utils import EMPTY_ETAG
|
Add force auth retry mode in swiftclient
This patch attemps to add an option to force get_auth call while retrying
an operation even if it gets errors other than 401 Unauthorized.
Why we need this:
The main reason why we need this is current python-swiftclient requests could
never get succeeded under certion situation using third party proxies/load balancers
between the client and swift-proxy server. I think, it would be general situation
of the use case.
Specifically describing nginx case, the nginx can close the socket from the client
when the response code from swift is not 2xx series. In default, nginx can wait the
buffers from the client for a while (default 30s)[1] but after the time past, nginx
will close the socket immediately. Unfortunately, if python-swiftclient has still been
sending the data into the socket, python-swiftclient will get socket error (EPIPE,
BrokenPipe). From the swiftclient perspective, this is absolutely not an auth error,
so current python-swiftclient will continue to retry without re-auth.
However, if the root cause is sort of 401 (i.e. nginx got 401 unauthorized from the
swift-proxy because of token expiration), swiftclient will loop 401 -> EPIPE -> 401...
until it consume the max retry times.
In particlar, less time to live of the token and multipart object upload with large
segments could not get succeeded as below:
Connection Model:
python-swiftclient -> nginx -> swift-proxy -> swift-backend
Case: Try to create slo with large segments and the auth token expired with 1 hour
1. client create a connection to nginx with successful response from swift-proxy and its auth
2. client continue to put large segment objects
(e.g. 1~5GB for each and the total would 20~30GB, i.e. 20~30 segments)
3. after some of segments uploaded, 1 hour past but client is still trying to
send remaining segment objects.
4. nginx got 401 from swift-proxy for a request and wait that the connection is closed
from the client but timeout past because the python-swiftclient is still sending much data
into the socket before reading the 401 response.
5. client got socket error because nginx closed the connection during sending the buffer.
6. client retries a new connection to nginx without re-auth...
<loop 4-6>
7. finally python-swiftclient failed with socket error (Broken Pipe)
In operational perspective, setting longer timeout for lingering close would be an option but
it's not complete solution because any other proxy/LB may not support the options.
If we actually do THE RIGHT THING in python-swiftclient, we should send expects: 100-continue
header and handle the first response to re-auth correctly.
HOWEVER, the current python's httplib and requests module used by python-swiftclient doesn't
support expects: 100-continue header [2] and the thread proposed a fix [3] is not super active.
And we know the reason we depends on the library is to fix a security issue that existed
in older python-swiftclient [4] so that we should touch around it super carefully.
In the reality, as the hot fix, this patch try to mitigate the unfortunate situation
described above WITHOUT 100-continue fix, just users can force to re-auth when any errors
occurred during the retries that can be accepted in the upstream.
1: http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close
2: https://github.com/requests/requests/issues/713
3: https://bugs.python.org/issue1346874
4: https://review.openstack.org/#/c/69187/
Change-Id: I3470b56e3f9cf9cdb8c2fc2a94b2c551927a3440
2018-03-12 17:54:17 +09:00
|
|
|
from swiftclient.exceptions import ClientException
|
2012-05-08 15:51:15 +01:00
|
|
|
from swiftclient import client as c
|
2014-02-18 11:28:04 +01:00
|
|
|
import swiftclient.utils
|
2014-11-06 14:10:13 +00:00
|
|
|
import swiftclient
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
|
2015-12-04 11:28:05 -08:00
|
|
|
class TestClientException(unittest.TestCase):
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def test_is_exception(self):
|
|
|
|
self.assertTrue(issubclass(c.ClientException, Exception))
|
|
|
|
|
|
|
|
def test_format(self):
|
|
|
|
exc = c.ClientException('something failed')
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIn('something failed', str(exc))
|
2012-05-08 15:51:15 +01:00
|
|
|
test_kwargs = (
|
|
|
|
'scheme',
|
|
|
|
'host',
|
|
|
|
'port',
|
|
|
|
'path',
|
|
|
|
'query',
|
|
|
|
'status',
|
|
|
|
'reason',
|
|
|
|
'device',
|
2016-01-18 17:05:28 -08:00
|
|
|
'response_content',
|
2012-05-08 15:51:15 +01:00
|
|
|
)
|
|
|
|
for value in test_kwargs:
|
|
|
|
kwargs = {
|
2013-03-22 14:59:57 +00:00
|
|
|
'http_%s' % value: value,
|
2012-05-08 15:51:15 +01:00
|
|
|
}
|
|
|
|
exc = c.ClientException('test', **kwargs)
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIn(value, str(exc))
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2016-01-18 17:05:28 -08:00
|
|
|
def test_attrs(self):
|
|
|
|
test_kwargs = (
|
|
|
|
'scheme',
|
|
|
|
'host',
|
|
|
|
'port',
|
|
|
|
'path',
|
|
|
|
'query',
|
|
|
|
'status',
|
|
|
|
'reason',
|
|
|
|
'device',
|
|
|
|
'response_content',
|
|
|
|
'response_headers',
|
|
|
|
)
|
|
|
|
for value in test_kwargs:
|
|
|
|
key = 'http_%s' % value
|
|
|
|
kwargs = {key: value}
|
|
|
|
exc = c.ClientException('test', **kwargs)
|
|
|
|
self.assertIs(True, hasattr(exc, key))
|
|
|
|
self.assertEqual(getattr(exc, key), value)
|
|
|
|
|
2020-08-14 10:41:15 -07:00
|
|
|
def test_transaction_id_from_headers(self):
|
|
|
|
exc = c.ClientException('test')
|
|
|
|
self.assertIsNone(exc.transaction_id)
|
|
|
|
|
|
|
|
exc = c.ClientException('test', http_response_headers={})
|
|
|
|
self.assertIsNone(exc.transaction_id)
|
|
|
|
|
|
|
|
exc = c.ClientException('test', http_response_headers={
|
|
|
|
'X-Trans-Id': 'some-id'})
|
|
|
|
self.assertEqual(exc.transaction_id, 'some-id')
|
|
|
|
self.assertIn('(txn: some-id)', str(exc))
|
|
|
|
|
|
|
|
exc = c.ClientException('test', http_response_headers={
|
|
|
|
'X-Openstack-Request-Id': 'some-other-id'})
|
|
|
|
self.assertEqual(exc.transaction_id, 'some-other-id')
|
|
|
|
self.assertIn('(txn: some-other-id)', str(exc))
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class MockHttpResponse:
|
2017-03-07 16:52:51 -08:00
|
|
|
def __init__(self, status=0, headers=None, verify=False):
|
2014-01-24 17:40:16 +01:00
|
|
|
self.status = status
|
|
|
|
self.status_code = status
|
2013-10-15 09:46:46 -07:00
|
|
|
self.reason = "OK"
|
2012-10-11 15:04:00 +03:00
|
|
|
self.buffer = []
|
2014-02-18 11:28:04 +01:00
|
|
|
self.requests_params = None
|
2014-10-29 10:11:29 +00:00
|
|
|
self.verify = verify
|
|
|
|
self.md5sum = md5()
|
2015-03-04 14:01:55 +00:00
|
|
|
self.headers = {'etag': '"%s"' % EMPTY_ETAG}
|
2014-10-29 10:11:29 +00:00
|
|
|
if headers:
|
|
|
|
self.headers.update(headers)
|
2015-02-25 10:58:27 +00:00
|
|
|
self.closed = False
|
2014-10-29 10:11:29 +00:00
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class Raw:
|
2015-04-26 01:37:15 -06:00
|
|
|
def __init__(self, headers):
|
|
|
|
self.headers = headers
|
|
|
|
|
|
|
|
def read(self, **kw):
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def getheader(self, name, default):
|
|
|
|
return self.headers.get(name, default)
|
|
|
|
|
|
|
|
self.raw = Raw(headers)
|
2014-01-24 17:40:16 +01:00
|
|
|
|
2012-10-11 15:04:00 +03:00
|
|
|
def read(self):
|
|
|
|
return ""
|
|
|
|
|
2015-04-26 01:37:15 -06:00
|
|
|
def close(self):
|
2015-02-25 10:58:27 +00:00
|
|
|
self.closed = True
|
2015-04-26 01:37:15 -06:00
|
|
|
|
2012-10-11 15:04:00 +03:00
|
|
|
def getheader(self, name, default):
|
2014-10-29 10:11:29 +00:00
|
|
|
return self.headers.get(name, default)
|
2012-10-11 15:04:00 +03:00
|
|
|
|
2013-10-15 09:46:46 -07:00
|
|
|
def getheaders(self):
|
2017-03-07 16:52:51 -08:00
|
|
|
return dict(self.headers).items()
|
2013-10-15 09:46:46 -07:00
|
|
|
|
2012-10-11 15:04:00 +03:00
|
|
|
def fake_response(self):
|
2014-10-29 10:11:29 +00:00
|
|
|
return self
|
2012-10-11 15:04:00 +03:00
|
|
|
|
2014-01-24 17:40:16 +01:00
|
|
|
def _fake_request(self, *arg, **kwarg):
|
|
|
|
self.status = 200
|
2014-02-18 11:28:04 +01:00
|
|
|
self.requests_params = kwarg
|
2014-10-29 10:11:29 +00:00
|
|
|
if self.verify:
|
|
|
|
for chunk in kwarg['data']:
|
|
|
|
self.md5sum.update(chunk)
|
|
|
|
|
2014-01-24 17:40:16 +01:00
|
|
|
# This simulate previous httplib implementation that would do a
|
|
|
|
# putrequest() and then use putheader() to send header.
|
2014-03-24 18:21:34 +01:00
|
|
|
for k, v in kwarg['headers'].items():
|
2014-04-02 14:34:50 +02:00
|
|
|
self.buffer.append((k, v))
|
2014-01-24 17:40:16 +01:00
|
|
|
return self.fake_response()
|
2012-10-11 15:04:00 +03:00
|
|
|
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
class TestHttpHelpers(MockHttpTest):
|
|
|
|
|
|
|
|
def test_quote(self):
|
2014-03-31 12:40:24 +02:00
|
|
|
value = b'bytes\xff'
|
|
|
|
self.assertEqual('bytes%FF', c.quote(value))
|
|
|
|
value = 'native string'
|
|
|
|
self.assertEqual('native%20string', c.quote(value))
|
2022-03-21 18:17:41 +00:00
|
|
|
value = 'unicode string'
|
2014-03-31 12:40:24 +02:00
|
|
|
self.assertEqual('unicode%20string', c.quote(value))
|
2022-03-21 18:17:41 +00:00
|
|
|
value = 'unicode:\xe9\u20ac'
|
2014-03-31 12:40:24 +02:00
|
|
|
self.assertEqual('unicode%3A%C3%A9%E2%82%AC', c.quote(value))
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-05-18 08:05:02 -07:00
|
|
|
def test_parse_header_string(self):
|
|
|
|
value = b'bytes'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('bytes', c.parse_header_string(value))
|
|
|
|
value = 'unicode:\xe9\u20ac'
|
|
|
|
self.assertEqual('unicode:\xe9\u20ac', c.parse_header_string(value))
|
2015-05-18 08:05:02 -07:00
|
|
|
value = 'native%20string'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('native string', c.parse_header_string(value))
|
2015-05-18 08:05:02 -07:00
|
|
|
|
|
|
|
value = b'encoded%20bytes%E2%82%AC'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('encoded bytes\u20ac', c.parse_header_string(value))
|
2015-05-18 08:05:02 -07:00
|
|
|
value = 'encoded%20unicode%E2%82%AC'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('encoded unicode\u20ac',
|
2015-05-18 08:05:02 -07:00
|
|
|
c.parse_header_string(value))
|
|
|
|
|
|
|
|
value = b'bad%20bytes%ff%E2%82%AC'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('bad%20bytes%ff%E2%82%AC',
|
2015-05-18 08:05:02 -07:00
|
|
|
c.parse_header_string(value))
|
2022-03-21 18:17:41 +00:00
|
|
|
value = 'bad%20unicode%ff\u20ac'
|
|
|
|
self.assertEqual('bad%20unicode%ff\u20ac',
|
2015-05-18 08:05:02 -07:00
|
|
|
c.parse_header_string(value))
|
|
|
|
|
|
|
|
value = b'really%20bad\xffbytes'
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('really%2520bad%FFbytes',
|
2015-05-18 08:05:02 -07:00
|
|
|
c.parse_header_string(value))
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_http_connection(self):
|
|
|
|
url = 'http://www.test.com'
|
|
|
|
_junk, conn = c.http_connection(url)
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIs(type(conn), c.HTTPConnection)
|
2012-05-08 15:51:15 +01:00
|
|
|
url = 'https://www.test.com'
|
|
|
|
_junk, conn = c.http_connection(url)
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIs(type(conn), c.HTTPConnection)
|
2012-05-08 15:51:15 +01:00
|
|
|
url = 'ftp://www.test.com'
|
|
|
|
self.assertRaises(c.ClientException, c.http_connection, url)
|
|
|
|
|
2014-11-06 14:10:13 +00:00
|
|
|
def test_encode_meta_headers(self):
|
|
|
|
headers = {'abc': '123',
|
2022-03-21 18:17:41 +00:00
|
|
|
'x-container-meta-\u0394': 123,
|
|
|
|
'x-account-meta-\u0394': 12.3,
|
|
|
|
'x-object-meta-\u0394': True}
|
2014-11-06 14:10:13 +00:00
|
|
|
|
|
|
|
r = swiftclient.encode_meta_headers(headers)
|
|
|
|
|
|
|
|
self.assertEqual(len(headers), len(r))
|
|
|
|
# ensure non meta headers are not encoded
|
2022-02-17 11:37:13 +00:00
|
|
|
self.assertIs(type(r.get('abc')), bytes)
|
2014-11-06 14:10:13 +00:00
|
|
|
del r['abc']
|
|
|
|
|
|
|
|
for k, v in r.items():
|
2022-02-17 11:37:13 +00:00
|
|
|
self.assertIs(type(k), bytes)
|
|
|
|
self.assertIs(type(v), bytes)
|
2016-08-25 10:51:29 -07:00
|
|
|
self.assertIn(v, (b'123', b'12.3', b'True'))
|
2014-11-06 14:10:13 +00:00
|
|
|
|
2014-03-28 15:45:37 -07:00
|
|
|
def test_set_user_agent_default(self):
|
|
|
|
_junk, conn = c.http_connection('http://www.example.com')
|
|
|
|
req_headers = {}
|
|
|
|
|
|
|
|
def my_request_handler(*a, **kw):
|
|
|
|
req_headers.update(kw.get('headers', {}))
|
|
|
|
conn._request = my_request_handler
|
|
|
|
|
|
|
|
# test the default
|
|
|
|
conn.request('GET', '/')
|
|
|
|
ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
|
2014-05-14 21:15:07 +02:00
|
|
|
self.assertTrue(ua.startswith('python-swiftclient-'))
|
2014-03-28 15:45:37 -07:00
|
|
|
|
|
|
|
def test_set_user_agent_per_request_override(self):
|
|
|
|
_junk, conn = c.http_connection('http://www.example.com')
|
|
|
|
req_headers = {}
|
|
|
|
|
|
|
|
def my_request_handler(*a, **kw):
|
|
|
|
req_headers.update(kw.get('headers', {}))
|
|
|
|
conn._request = my_request_handler
|
|
|
|
|
|
|
|
# test if it's actually set
|
|
|
|
conn.request('GET', '/', headers={'User-Agent': 'Me'})
|
|
|
|
ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
|
2014-04-15 16:28:50 -04:00
|
|
|
self.assertEqual(ua, b'Me', req_headers)
|
2014-03-28 15:45:37 -07:00
|
|
|
|
|
|
|
def test_set_user_agent_default_override(self):
|
|
|
|
_junk, conn = c.http_connection(
|
|
|
|
'http://www.example.com',
|
|
|
|
default_user_agent='a-new-default')
|
|
|
|
req_headers = {}
|
|
|
|
|
|
|
|
def my_request_handler(*a, **kw):
|
|
|
|
req_headers.update(kw.get('headers', {}))
|
|
|
|
conn._request = my_request_handler
|
|
|
|
|
|
|
|
# test setting a default
|
|
|
|
conn._request = my_request_handler
|
|
|
|
conn.request('GET', '/')
|
|
|
|
ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
|
|
|
|
self.assertEqual(ua, 'a-new-default')
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestGetAuth(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf')
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(url)
|
|
|
|
self.assertIsNone(token)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2012-06-06 18:38:08 +02:00
|
|
|
def test_invalid_auth(self):
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
'http://www.tests.com', 'asdf', 'asdf',
|
|
|
|
auth_version="foo")
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_auth_v1(self):
|
2014-02-13 23:33:01 -08:00
|
|
|
c.http_connection = self.fake_http_connection(200, auth_v1=True)
|
2012-05-08 15:51:15 +01:00
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
auth_version="1.0")
|
2014-02-13 23:33:01 -08:00
|
|
|
self.assertEqual(url, 'storageURL')
|
|
|
|
self.assertEqual(token, 'someauthtoken')
|
|
|
|
|
|
|
|
def test_auth_v1_insecure(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(200, 200, auth_v1=True)
|
2014-02-13 23:33:01 -08:00
|
|
|
url, token = c.get_auth('http://www.test.com/invalid_cert',
|
|
|
|
'asdf', 'asdf',
|
|
|
|
auth_version='1.0',
|
|
|
|
insecure=True)
|
|
|
|
self.assertEqual(url, 'storageURL')
|
|
|
|
self.assertEqual(token, 'someauthtoken')
|
|
|
|
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.get_auth('http://www.test.com/invalid_cert',
|
|
|
|
'asdf', 'asdf', auth_version='1.0')
|
2014-10-24 01:02:53 -07:00
|
|
|
# TODO: this test is really on validating the mock and not the
|
|
|
|
# the full plumbing into the requests's 'verify' option
|
2015-12-04 11:28:05 -08:00
|
|
|
self.assertIn('invalid_certificate', str(exc_context.exception))
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-06-02 21:02:42 +01:00
|
|
|
def test_auth_v1_timeout(self):
|
|
|
|
# this test has some overlap with
|
|
|
|
# TestConnection.test_timeout_passed_down but is required to check that
|
|
|
|
# get_auth does the right thing when it is not passed a timeout arg
|
|
|
|
orig_http_connection = c.http_connection
|
|
|
|
timeouts = []
|
|
|
|
|
|
|
|
def fake_request_handler(*a, **kw):
|
|
|
|
if 'timeout' in kw:
|
|
|
|
timeouts.append(kw['timeout'])
|
|
|
|
else:
|
|
|
|
timeouts.append(None)
|
|
|
|
return MockHttpResponse(
|
|
|
|
status=200,
|
|
|
|
headers={
|
|
|
|
'x-auth-token': 'a_token',
|
|
|
|
'x-storage-url': 'http://files.example.com/v1/AUTH_user'})
|
|
|
|
|
|
|
|
def fake_connection(*a, **kw):
|
|
|
|
url, conn = orig_http_connection(*a, **kw)
|
|
|
|
conn._request = fake_request_handler
|
|
|
|
return url, conn
|
|
|
|
|
|
|
|
with mock.patch('swiftclient.client.http_connection', fake_connection):
|
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
auth_version="1.0", timeout=42.0)
|
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
auth_version="1.0", timeout=None)
|
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
auth_version="1.0")
|
|
|
|
|
|
|
|
self.assertEqual(timeouts, [42.0, None, None])
|
|
|
|
|
|
|
|
def test_auth_v2_timeout(self):
|
|
|
|
# this test has some overlap with
|
|
|
|
# TestConnection.test_timeout_passed_down but is required to check that
|
|
|
|
# get_auth does the right thing when it is not passed a timeout arg
|
|
|
|
fake_ks = FakeKeystone(endpoint='http://some_url', token='secret')
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v2', fake_ks):
|
2015-06-02 21:02:42 +01:00
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=dict(tenant_name='tenant'),
|
|
|
|
auth_version="2.0", timeout=42.0)
|
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=dict(tenant_name='tenant'),
|
|
|
|
auth_version="2.0", timeout=None)
|
|
|
|
c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=dict(tenant_name='tenant'),
|
|
|
|
auth_version="2.0")
|
|
|
|
self.assertEqual(3, len(fake_ks.calls))
|
|
|
|
timeouts = [call['timeout'] for call in fake_ks.calls]
|
|
|
|
self.assertEqual([42.0, None, None], timeouts)
|
|
|
|
|
2014-05-20 13:35:03 +01:00
|
|
|
def test_auth_v2_with_tenant_name(self):
|
2013-03-22 14:59:57 +00:00
|
|
|
os_options = {'tenant_name': 'asdf'}
|
2014-03-25 08:21:21 +00:00
|
|
|
req_args = {'auth_version': '2.0'}
|
2016-08-24 23:10:59 +10:00
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2012-05-08 15:51:15 +01:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2014-05-20 13:35:03 +01:00
|
|
|
def test_auth_v2_with_tenant_id(self):
|
|
|
|
os_options = {'tenant_id': 'asdf'}
|
2014-03-25 08:21:21 +00:00
|
|
|
req_args = {'auth_version': '2.0'}
|
2016-08-24 23:10:59 +10:00
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2014-03-25 08:21:21 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
def test_auth_v2_with_project_name(self):
|
|
|
|
os_options = {'project_name': 'asdf'}
|
|
|
|
req_args = {'auth_version': '2.0'}
|
2016-08-24 23:10:59 +10:00
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2014-03-25 08:21:21 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
def test_auth_v2_with_project_id(self):
|
|
|
|
os_options = {'project_id': 'asdf'}
|
|
|
|
req_args = {'auth_version': '2.0'}
|
2016-08-24 23:10:59 +10:00
|
|
|
|
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2014-05-20 13:35:03 +01:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
def test_auth_v2_no_tenant_name_or_tenant_id(self):
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone({})):
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
'http://www.tests.com', 'asdf', 'asdf',
|
|
|
|
os_options={},
|
|
|
|
auth_version='2.0')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2014-05-20 13:35:03 +01:00
|
|
|
def test_auth_v2_with_tenant_name_none_and_tenant_id_none(self):
|
|
|
|
os_options = {'tenant_name': None,
|
|
|
|
'tenant_id': None}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options)):
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
'http://www.tests.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version='2.0')
|
2014-05-20 13:35:03 +01:00
|
|
|
|
2012-06-14 14:28:09 +00:00
|
|
|
def test_auth_v2_with_tenant_user_in_user(self):
|
2012-08-28 10:25:42 -04:00
|
|
|
tenant_option = {'tenant_name': 'foo'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(tenant_option)):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'foo:bar', 'asdf',
|
|
|
|
os_options={},
|
|
|
|
auth_version="2.0")
|
2012-07-04 21:46:02 +02:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
def test_auth_v2_tenant_name_no_os_options(self):
|
2012-08-28 10:25:42 -04:00
|
|
|
tenant_option = {'tenant_name': 'asdf'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(tenant_option)):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
tenant_name='asdf',
|
|
|
|
os_options={},
|
|
|
|
auth_version="2.0")
|
2012-06-14 14:28:09 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2012-08-28 10:25:42 -04:00
|
|
|
def test_auth_v2_with_os_options(self):
|
2013-03-22 14:59:57 +00:00
|
|
|
os_options = {'service_type': 'object-store',
|
|
|
|
'endpoint_type': 'internalURL',
|
|
|
|
'tenant_name': 'asdf'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options)):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2012-08-28 10:25:42 -04:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2012-09-19 15:47:36 +00:00
|
|
|
def test_auth_v2_with_tenant_user_in_user_no_os_options(self):
|
|
|
|
tenant_option = {'tenant_name': 'foo'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(tenant_option)):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'foo:bar', 'asdf',
|
|
|
|
auth_version="2.0")
|
2012-09-19 15:47:36 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2012-09-05 15:55:53 +01:00
|
|
|
def test_auth_v2_with_os_region_name(self):
|
2013-03-22 14:59:57 +00:00
|
|
|
os_options = {'region_name': 'good-region',
|
|
|
|
'tenant_name': 'asdf'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options)):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2.0")
|
2012-09-05 15:55:53 +01:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
def test_auth_v2_no_endpoint(self):
|
2013-03-22 14:59:57 +00:00
|
|
|
os_options = {'region_name': 'unknown_region',
|
|
|
|
'tenant_name': 'asdf'}
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options, c.ClientException)):
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
'http://www.tests.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2012-09-17 16:42:16 +01:00
|
|
|
def test_auth_v2_ks_exception(self):
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone({}, c.ClientException)):
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
'http://www.tests.com', 'asdf', 'asdf',
|
|
|
|
os_options={},
|
|
|
|
auth_version='2.0')
|
2012-09-17 16:42:16 +01:00
|
|
|
|
2012-12-19 09:52:54 -06:00
|
|
|
def test_auth_v2_cacert(self):
|
|
|
|
os_options = {'tenant_name': 'foo'}
|
2013-03-22 14:59:57 +00:00
|
|
|
auth_url_secure = 'https://www.tests.com'
|
2012-12-19 09:52:54 -06:00
|
|
|
auth_url_insecure = 'https://www.tests.com/self-signed-certificate'
|
|
|
|
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options, None)):
|
|
|
|
url, token = c.get_auth(auth_url_secure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
insecure=False)
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
url, token = c.get_auth(auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
cacert='ca.pem', insecure=False)
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
insecure=False)
|
2012-12-19 09:52:54 -06:00
|
|
|
|
2012-12-05 13:18:27 +09:00
|
|
|
def test_auth_v2_insecure(self):
|
|
|
|
os_options = {'tenant_name': 'foo'}
|
2013-03-22 14:59:57 +00:00
|
|
|
auth_url_secure = 'https://www.tests.com'
|
2012-12-05 13:18:27 +09:00
|
|
|
auth_url_insecure = 'https://www.tests.com/invalid-certificate'
|
|
|
|
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options, None)):
|
|
|
|
url, token = c.get_auth(auth_url_secure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
url, token = c.get_auth(auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
insecure=True)
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_insecure, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
insecure=False)
|
2012-12-05 13:18:27 +09:00
|
|
|
|
2016-04-10 23:18:17 +02:00
|
|
|
def test_auth_v2_cert(self):
|
|
|
|
os_options = {'tenant_name': 'foo'}
|
|
|
|
auth_url_no_sslauth = 'https://www.tests.com'
|
|
|
|
auth_url_sslauth = 'https://www.tests.com/client-certificate'
|
|
|
|
|
2016-08-24 23:10:59 +10:00
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone',
|
|
|
|
fake_get_auth_keystone(os_options, None)):
|
|
|
|
url, token = c.get_auth(auth_url_no_sslauth, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
url, token = c.get_auth(auth_url_sslauth, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
cert='minnie', cert_key='mickey')
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_sslauth, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0')
|
|
|
|
self.assertRaises(c.ClientException, c.get_auth,
|
|
|
|
auth_url_sslauth, 'asdf', 'asdf',
|
|
|
|
os_options=os_options, auth_version='2.0',
|
|
|
|
cert='minnie')
|
2016-04-10 23:18:17 +02:00
|
|
|
|
2014-03-25 08:21:21 +00:00
|
|
|
def test_auth_v3_with_tenant_name(self):
|
|
|
|
# check the correct auth version is passed to get_auth_keystone
|
|
|
|
os_options = {'tenant_name': 'asdf'}
|
|
|
|
req_args = {'auth_version': '3'}
|
2016-08-24 23:10:59 +10:00
|
|
|
|
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="3")
|
|
|
|
|
2014-03-25 08:21:21 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2019-12-18 00:32:36 +08:00
|
|
|
def test_auth_v3applicationcredential(self):
|
2023-07-07 14:32:05 -07:00
|
|
|
try:
|
|
|
|
from keystoneauth1 import exceptions as ksauthexceptions
|
|
|
|
except ImportError:
|
|
|
|
raise unittest.SkipTest('keystoneauth1 is not available')
|
2019-12-18 00:32:36 +08:00
|
|
|
|
|
|
|
os_options = {
|
|
|
|
"auth_type": "v3applicationcredential",
|
|
|
|
"application_credential_id": "proejct_id",
|
|
|
|
"application_credential_secret": "secret"}
|
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class FakeEndpointData:
|
2019-12-18 00:32:36 +08:00
|
|
|
catalog_url = 'http://swift.cluster/v1/KEY_project_id'
|
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class FakeKeystoneuth1v3Session:
|
2019-12-18 00:32:36 +08:00
|
|
|
|
|
|
|
def __init__(self, auth):
|
|
|
|
self.auth = auth
|
|
|
|
self.token = 'token'
|
|
|
|
|
|
|
|
def get_token(self):
|
|
|
|
if self.auth.auth_url == 'http://keystone:5000/v3':
|
|
|
|
return self.token
|
|
|
|
elif self.auth.auth_url == 'http://keystone:9000/v3':
|
|
|
|
raise ksauthexceptions.AuthorizationFailure
|
|
|
|
else:
|
|
|
|
raise ksauthexceptions.Unauthorized
|
|
|
|
|
|
|
|
def get_endpoint_data(self, service_type, endpoint_type, **kwargs):
|
|
|
|
return FakeEndpointData()
|
|
|
|
|
|
|
|
mock_sess = FakeKeystoneuth1v3Session
|
|
|
|
with mock.patch('keystoneauth1.session.Session', mock_sess):
|
|
|
|
url, token = c.get_auth('http://keystone:5000', '', '',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="3")
|
|
|
|
|
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertEqual(url, 'http://swift.cluster/v1/KEY_project_id')
|
|
|
|
self.assertEqual(token, 'token')
|
|
|
|
|
|
|
|
with mock.patch('keystoneauth1.session.Session', mock_sess):
|
|
|
|
with self.assertRaises(c.ClientException) as exc_mgr:
|
|
|
|
url, token = c.get_auth('http://keystone:9000', '', '',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="3")
|
|
|
|
|
|
|
|
body = 'Unauthorized. Check application credential id and secret.'
|
|
|
|
body = 'Authorization Failure. Cannot authorize API client.'
|
|
|
|
self.assertEqual(exc_mgr.exception.__str__()[-89:], body)
|
|
|
|
|
|
|
|
with mock.patch('keystoneauth1.session.Session', mock_sess):
|
|
|
|
with self.assertRaises(c.ClientException) as exc_mgr:
|
|
|
|
url, token = c.get_auth('http://keystone:5000', '', '',
|
|
|
|
os_options=os_options,
|
|
|
|
auth_version="2")
|
|
|
|
|
|
|
|
body = 'Unauthorized. Check application credential id and secret.'
|
|
|
|
self.assertEqual(exc_mgr.exception.__str__()[-89:], body)
|
|
|
|
|
2014-03-25 08:21:21 +00:00
|
|
|
def test_get_keystone_client_2_0(self):
|
|
|
|
# check the correct auth version is passed to get_auth_keystone
|
|
|
|
os_options = {'tenant_name': 'asdf'}
|
|
|
|
req_args = {'auth_version': '2.0'}
|
2016-08-24 23:10:59 +10:00
|
|
|
|
|
|
|
ks = fake_get_auth_keystone(os_options, required_kwargs=req_args)
|
|
|
|
with mock.patch('swiftclient.client.get_auth_keystone', ks):
|
|
|
|
url, token = c.get_keystoneclient_2_0('http://www.test.com',
|
|
|
|
'asdf', 'asdf',
|
|
|
|
os_options=os_options)
|
|
|
|
|
2014-03-25 08:21:21 +00:00
|
|
|
self.assertTrue(url.startswith("http"))
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2017-06-13 10:51:33 +02:00
|
|
|
def test_get_auth_keystone_versionless(self):
|
|
|
|
fake_ks = FakeKeystone(endpoint='http://some_url', token='secret')
|
|
|
|
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v3', fake_ks):
|
2017-06-13 10:51:33 +02:00
|
|
|
c.get_auth_keystone('http://authurl', 'user', 'key', {})
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual('http://authurl/v3', fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
|
|
|
def test_get_auth_keystone_versionless_auth_version_set(self):
|
|
|
|
fake_ks = FakeKeystone(endpoint='http://some_url', token='secret')
|
|
|
|
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v2', fake_ks):
|
2017-06-13 10:51:33 +02:00
|
|
|
c.get_auth_keystone('http://auth_url', 'user', 'key',
|
|
|
|
{}, auth_version='2.0')
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual('http://auth_url/v2.0',
|
|
|
|
fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
2017-06-13 11:14:52 -07:00
|
|
|
def test_get_auth_keystone_versionful(self):
|
|
|
|
fake_ks = FakeKeystone(endpoint='http://some_url', token='secret')
|
|
|
|
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v3', fake_ks):
|
2017-06-13 11:14:52 -07:00
|
|
|
c.get_auth_keystone('http://auth_url/v3', 'user', 'key',
|
|
|
|
{}, auth_version='3')
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual('http://auth_url/v3',
|
|
|
|
fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
|
|
|
def test_get_auth_keystone_devstack_versionful(self):
|
|
|
|
fake_ks = FakeKeystone(
|
|
|
|
endpoint='http://storage.example.com/v1/AUTH_user', token='secret')
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v3', fake_ks):
|
2017-06-13 11:14:52 -07:00
|
|
|
c.get_auth_keystone('https://192.168.8.8/identity/v3',
|
|
|
|
'user', 'key', {}, auth_version='3')
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual('https://192.168.8.8/identity/v3',
|
|
|
|
fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
|
|
|
def test_get_auth_keystone_devstack_versionless(self):
|
|
|
|
fake_ks = FakeKeystone(
|
|
|
|
endpoint='http://storage.example.com/v1/AUTH_user', token='secret')
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v3', fake_ks):
|
2017-06-13 11:14:52 -07:00
|
|
|
c.get_auth_keystone('https://192.168.8.8/identity',
|
|
|
|
'user', 'key', {}, auth_version='3')
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual('https://192.168.8.8/identity/v3',
|
|
|
|
fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
|
|
|
def test_auth_keystone_url_some_junk_nonsense(self):
|
|
|
|
fake_ks = FakeKeystone(
|
|
|
|
endpoint='http://storage.example.com/v1/AUTH_user',
|
|
|
|
token='secret')
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v3', fake_ks):
|
2017-06-13 11:14:52 -07:00
|
|
|
c.get_auth_keystone('http://blah.example.com/v2moo',
|
|
|
|
'user', 'key', {}, auth_version='3')
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
# v2 looks sorta version-y, but it's not an exact match, so this is
|
|
|
|
# probably about just as bad as anything else we might guess at
|
|
|
|
self.assertEqual('http://blah.example.com/v2moo/v3',
|
|
|
|
fake_ks.calls[0].get('auth_url'))
|
|
|
|
|
2016-03-29 19:10:42 +00:00
|
|
|
def test_auth_with_session(self):
|
|
|
|
mock_session = mock.MagicMock()
|
|
|
|
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
|
|
|
|
mock_session.get_token.return_value = 'token'
|
|
|
|
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
session=mock_session)
|
|
|
|
self.assertEqual(url, 'http://storagehost/v1/acct')
|
|
|
|
self.assertTrue(token)
|
|
|
|
|
2012-12-05 13:18:27 +09:00
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
class TestGetAccount(MockHttpTest):
|
|
|
|
|
|
|
|
def test_no_content(self):
|
|
|
|
c.http_connection = self.fake_http_connection(204)
|
2015-09-22 12:14:18 -07:00
|
|
|
value = c.get_account('http://www.test.com/v1/acct', 'asdf')[1]
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(value, [])
|
2015-09-22 12:14:18 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-05-02 00:15:11 +09:00
|
|
|
def test_param_marker(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&marker=marker")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_account('http://www.test.com/v1/acct', 'asdf', marker='marker')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json&marker=marker', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_limit(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&limit=10")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_account('http://www.test.com/v1/acct', 'asdf', limit=10)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json&limit=10', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_prefix(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&prefix=asdf/")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_account('http://www.test.com/v1/acct', 'asdf', prefix='asdf/')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json&prefix=asdf/', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_end_marker(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&end_marker=end_marker")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_account('http://www.test.com/v1/acct', 'asdf',
|
|
|
|
end_marker='end_marker')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json&end_marker=end_marker', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
2018-11-23 22:47:15 -08:00
|
|
|
def test_param_delimiter(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&delimiter=-")
|
|
|
|
c.get_account('http://www.test.com/v1/acct', 'asdf',
|
|
|
|
delimiter='-')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct?format=json&delimiter=-', '', {
|
|
|
|
'accept-encoding': 'gzip',
|
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestHeadAccount(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(200, headers={
|
|
|
|
'x-account-meta-color': 'blue',
|
|
|
|
})
|
|
|
|
resp_headers = c.head_account('http://www.tests.com', 'asdf')
|
|
|
|
self.assertEqual(resp_headers['x-account-meta-color'], 'blue')
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', 'http://www.tests.com', '', {'x-auth-token': 'asdf'})
|
|
|
|
])
|
2020-04-18 22:41:55 -07:00
|
|
|
self.assertTrue(self.request_log[-1][-1]._closed)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 65
|
2016-01-18 17:05:28 -08:00
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.head_account('http://www.tests.com', 'asdf')
|
|
|
|
e = exc_context.exception
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertEqual(e.http_response_content, body)
|
|
|
|
self.assertEqual(e.http_status, 500)
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', 'http://www.tests.com', '', {'x-auth-token': 'asdf'})
|
|
|
|
])
|
|
|
|
# TODO: this is a fairly brittle test of the __repr__ on the
|
|
|
|
# ClientException which should probably be in a targeted test
|
|
|
|
new_body = "[first 60 chars of response] " + body[0:60]
|
|
|
|
self.assertEqual(e.__str__()[-89:], new_body)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
|
2015-06-11 14:33:39 -07:00
|
|
|
class TestPostAccount(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200, headers={
|
|
|
|
'X-Account-Meta-Color': 'blue',
|
|
|
|
}, body='foo')
|
2017-08-25 12:13:12 -07:00
|
|
|
headers = {'x-account-meta-shape': 'square'}
|
2015-06-11 14:33:39 -07:00
|
|
|
resp_headers, body = c.post_account(
|
|
|
|
'http://www.tests.com/path/to/account', 'asdf',
|
2017-08-25 12:13:12 -07:00
|
|
|
headers, query_string='bar=baz',
|
2015-06-11 14:33:39 -07:00
|
|
|
data='some data')
|
|
|
|
self.assertEqual('blue', resp_headers.get('x-account-meta-color'))
|
|
|
|
self.assertEqual('foo', body)
|
|
|
|
self.assertRequests([
|
|
|
|
('POST', 'http://www.tests.com/path/to/account?bar=baz',
|
|
|
|
'some data', {'x-auth-token': 'asdf',
|
|
|
|
'x-account-meta-shape': 'square'})
|
|
|
|
])
|
2017-08-25 12:13:12 -07:00
|
|
|
# Check that we didn't mutate the request ehader dict
|
|
|
|
self.assertEqual(headers, {'x-account-meta-shape': 'square'})
|
2015-06-11 14:33:39 -07:00
|
|
|
|
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 65
|
|
|
|
c.http_connection = self.fake_http_connection(500, body=body)
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_mgr:
|
|
|
|
c.post_account('http://www.tests.com', 'asdf', {})
|
|
|
|
self.assertEqual(exc_mgr.exception.http_response_content, body)
|
|
|
|
self.assertEqual(exc_mgr.exception.http_status, 500)
|
2015-06-11 14:33:39 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('POST', 'http://www.tests.com', None, {'x-auth-token': 'asdf'})
|
|
|
|
])
|
|
|
|
# TODO: this is a fairly brittle test of the __repr__ on the
|
|
|
|
# ClientException which should probably be in a targeted test
|
|
|
|
new_body = "[first 60 chars of response] " + body[0:60]
|
2015-12-04 11:28:05 -08:00
|
|
|
self.assertEqual(exc_mgr.exception.__str__()[-89:], new_body)
|
2015-06-11 14:33:39 -07:00
|
|
|
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
class TestGetContainer(MockHttpTest):
|
|
|
|
|
|
|
|
def test_no_content(self):
|
|
|
|
c.http_connection = self.fake_http_connection(204)
|
2015-09-22 12:14:18 -07:00
|
|
|
value = c.get_container('http://www.test.com/v1/acct', 'token',
|
|
|
|
'container')[1]
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(value, [])
|
2015-09-22 12:14:18 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-05-02 00:15:11 +09:00
|
|
|
def test_param_marker(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&marker=marker")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
|
|
|
marker='marker')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&marker=marker', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_limit(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&limit=10")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
|
|
|
limit=10)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&limit=10', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_prefix(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&prefix=asdf/")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
|
|
|
prefix='asdf/')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&prefix=asdf/', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_delimiter(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&delimiter=/")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
|
|
|
delimiter='/')
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&delimiter=/', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_end_marker(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&end_marker=end_marker")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
2013-05-02 00:15:11 +09:00
|
|
|
end_marker='end_marker')
|
2015-09-22 12:14:18 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&end_marker=end_marker',
|
2015-05-21 22:44:36 -07:00
|
|
|
'', {'x-auth-token': 'token', 'accept-encoding': 'gzip'}),
|
2015-09-22 12:14:18 -07:00
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
|
|
|
def test_param_path(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204,
|
|
|
|
query_string="format=json&path=asdf")
|
2015-09-22 12:14:18 -07:00
|
|
|
c.get_container('http://www.test.com/v1/acct', 'token', 'container',
|
2013-05-02 00:15:11 +09:00
|
|
|
path='asdf')
|
2015-09-22 12:14:18 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/acct/container?format=json&path=asdf', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 12:14:18 -07:00
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2013-05-02 00:15:11 +09:00
|
|
|
|
2015-09-15 14:34:53 +05:30
|
|
|
def test_request_headers(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
204, query_string="format=json")
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
headers = {'x-client-key': 'client key'}
|
|
|
|
c.get_container('url_is_irrelevant', 'TOKEN', 'container',
|
|
|
|
http_conn=conn, headers=headers)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/container?format=json', '', {
|
|
|
|
'x-auth-token': 'TOKEN',
|
|
|
|
'x-client-key': 'client key',
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-15 14:34:53 +05:30
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
2016-02-23 15:02:03 -06:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
200, query_string="format=json&hello=20", body=b'[]')
|
|
|
|
c.get_container('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
query_string="hello=20")
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/asdf?format=json&hello=20', '', {
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2016-02-23 15:02:03 -06:00
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestHeadContainer(MockHttpTest):
|
|
|
|
|
2014-10-24 01:02:53 -07:00
|
|
|
def test_head_ok(self):
|
|
|
|
fake_conn = self.fake_http_connection(
|
|
|
|
200, headers={'x-container-meta-color': 'blue'})
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
new=fake_conn):
|
|
|
|
resp = c.head_container('https://example.com/v1/AUTH_test',
|
|
|
|
'token', 'container')
|
|
|
|
self.assertEqual(resp['x-container-meta-color'], 'blue')
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', 'https://example.com/v1/AUTH_test/container', '',
|
|
|
|
{'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 60
|
2016-01-18 17:05:28 -08:00
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.head_container('http://www.test.com', 'asdf', 'container')
|
|
|
|
e = exc_context.exception
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/container', '', {'x-auth-token': 'asdf'}),
|
|
|
|
])
|
|
|
|
self.assertEqual(e.http_status, 500)
|
|
|
|
self.assertEqual(e.http_response_content, body)
|
2016-01-18 17:05:28 -08:00
|
|
|
self.assertEqual(e.http_response_headers, headers)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestPutContainer(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
2015-09-23 09:55:10 +01:00
|
|
|
value = c.put_container('http://www.test.com', 'token', 'container')
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(value)
|
2015-09-23 09:55:10 +01:00
|
|
|
self.assertRequests([
|
|
|
|
('PUT', '/container', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'content-length': '0'}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 60
|
2016-01-18 17:05:28 -08:00
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.put_container('http://www.test.com', 'token', 'container')
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
2016-01-18 17:05:28 -08:00
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertRequests([
|
2015-09-22 12:14:18 -07:00
|
|
|
('PUT', '/container', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'content-length': '0'}),
|
2014-10-24 01:02:53 -07:00
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2016-02-23 15:02:03 -06:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200,
|
|
|
|
query_string="hello=20")
|
|
|
|
c.put_container('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
query_string="hello=20")
|
|
|
|
for req in self.iter_request_log():
|
|
|
|
self.assertEqual(req['method'], 'PUT')
|
|
|
|
self.assertEqual(req['parsed_path'].path, '/asdf')
|
|
|
|
self.assertEqual(req['parsed_path'].query, 'hello=20')
|
|
|
|
self.assertEqual(req['headers']['x-auth-token'], 'asdf')
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestDeleteContainer(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
2015-09-23 09:55:10 +01:00
|
|
|
value = c.delete_container('http://www.test.com', 'token', 'container')
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(value)
|
2015-09-23 09:55:10 +01:00
|
|
|
self.assertRequests([
|
|
|
|
('DELETE', '/container', '', {
|
|
|
|
'x-auth-token': 'token'}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2016-02-23 15:02:03 -06:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200,
|
|
|
|
query_string="hello=20")
|
|
|
|
c.delete_container('http://www.test.com', 'token', 'container',
|
|
|
|
query_string="hello=20")
|
|
|
|
self.assertRequests([
|
|
|
|
('DELETE', 'http://www.test.com/container?hello=20', '', {
|
|
|
|
'x-auth-token': 'token'})
|
|
|
|
])
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestGetObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_server_error(self):
|
2016-01-18 17:05:28 -08:00
|
|
|
body = 'c' * 60
|
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.get_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-03-05 15:12:04 -08:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200,
|
|
|
|
query_string="hello=20")
|
|
|
|
c.get_object('http://www.test.com', 'asdf', 'asdf', 'asdf',
|
|
|
|
query_string="hello=20")
|
2015-09-22 12:14:18 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/asdf/asdf?hello=20', '', {
|
|
|
|
'x-auth-token': 'asdf'}),
|
|
|
|
])
|
2013-03-05 15:12:04 -08:00
|
|
|
|
2015-12-15 16:54:04 +00:00
|
|
|
def test_get_object_as_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200, body='abcde')
|
|
|
|
__, resp = c.get_object('http://storage.example.com', 'TOKEN',
|
|
|
|
'container_name', 'object_name')
|
|
|
|
self.assertEqual(resp, 'abcde')
|
|
|
|
|
2013-07-27 17:06:28 +10:00
|
|
|
def test_request_headers(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
2013-07-27 17:06:28 +10:00
|
|
|
headers = {'Range': 'bytes=1-2'}
|
|
|
|
c.get_object('url_is_irrelevant', 'TOKEN', 'container', 'object',
|
|
|
|
http_conn=conn, headers=headers)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/container/object', '', {
|
|
|
|
'x-auth-token': 'TOKEN',
|
|
|
|
'range': 'bytes=1-2',
|
|
|
|
}),
|
|
|
|
])
|
2013-07-27 17:06:28 +10:00
|
|
|
|
2015-05-18 08:05:02 -07:00
|
|
|
def test_response_headers(self):
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
200, headers={'X-Utf-8-Header': b't%c3%a9st',
|
|
|
|
'X-Non-Utf-8-Header': b'%ff',
|
|
|
|
'X-Binary-Header': b'\xff'})
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
headers, data = c.get_object('url_is_irrelevant', 'TOKEN',
|
|
|
|
'container', 'object', http_conn=conn)
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('t\xe9st', headers.get('x-utf-8-header', ''))
|
|
|
|
self.assertEqual('%ff', headers.get('x-non-utf-8-header', ''))
|
|
|
|
self.assertEqual('%FF', headers.get('x-binary-header', ''))
|
2015-05-18 08:05:02 -07:00
|
|
|
|
2015-02-12 12:34:07 +00:00
|
|
|
def test_chunk_size_read_method(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(200, body='abcde')
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=3)
|
|
|
|
self.assertTrue(hasattr(resp, 'read'))
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertEqual(resp.read(3), 'abc')
|
|
|
|
self.assertEqual(resp.read(None), 'de')
|
|
|
|
self.assertEqual(resp.read(), '')
|
2015-02-12 12:34:07 +00:00
|
|
|
|
|
|
|
def test_chunk_size_iter(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(200, body='abcde')
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=3)
|
|
|
|
self.assertTrue(hasattr(resp, 'next'))
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertEqual(next(resp), 'abc')
|
|
|
|
self.assertEqual(next(resp), 'de')
|
2015-02-12 12:34:07 +00:00
|
|
|
self.assertRaises(StopIteration, next, resp)
|
|
|
|
|
|
|
|
def test_chunk_size_read_and_iter(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(200, body='abcdef')
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
|
|
|
|
self.assertTrue(hasattr(resp, 'read'))
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertEqual(resp.read(3), 'abc')
|
|
|
|
self.assertEqual(next(resp), 'de')
|
|
|
|
self.assertEqual(resp.read(), 'f')
|
2015-02-12 12:34:07 +00:00
|
|
|
self.assertRaises(StopIteration, next, resp)
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertEqual(resp.read(), '')
|
2015-02-12 12:34:07 +00:00
|
|
|
|
2016-01-08 11:31:32 -08:00
|
|
|
def test_chunk_size_iter_chunked_no_retry(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url/', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
200, body='abcdef', headers={'Transfer-Encoding': 'chunked'})
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
|
|
|
|
self.assertEqual(next(resp), 'ab')
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertRaises(StopIteration, next, resp)
|
|
|
|
|
|
|
|
def test_chunk_size_iter_retry(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(200, 'abcdef', {'etag': 'some etag',
|
|
|
|
'content-length': '6'}),
|
|
|
|
StubResponse(206, 'cdef', {'etag': 'some etag',
|
2016-01-18 11:38:44 -08:00
|
|
|
'content-length': '4',
|
|
|
|
'content-range': 'bytes 2-5/6'}),
|
2016-01-08 11:31:32 -08:00
|
|
|
StubResponse(206, 'ef', {'etag': 'some etag',
|
2016-01-18 11:38:44 -08:00
|
|
|
'content-length': '2',
|
|
|
|
'content-range': 'bytes 4-5/6'}),
|
2016-01-08 11:31:32 -08:00
|
|
|
)
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
|
|
|
|
self.assertEqual(next(resp), 'ab')
|
|
|
|
self.assertEqual(1, conn.attempts)
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertEqual(next(resp), 'cd')
|
|
|
|
self.assertEqual(2, conn.attempts)
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertEqual(next(resp), 'ef')
|
|
|
|
self.assertEqual(3, conn.attempts)
|
|
|
|
self.assertRaises(StopIteration, next, resp)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'range': 'bytes=2-',
|
|
|
|
'if-match': 'some etag',
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'range': 'bytes=4-',
|
|
|
|
'if-match': 'some etag',
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
2016-01-18 11:38:44 -08:00
|
|
|
def test_chunk_size_iter_retry_no_range_support(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(*[
|
|
|
|
StubResponse(200, 'abcdef', {'etag': 'some etag',
|
|
|
|
'content-length': '6'})
|
|
|
|
] * 3)
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
|
|
|
|
self.assertEqual(next(resp), 'ab')
|
|
|
|
self.assertEqual(1, conn.attempts)
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertEqual(next(resp), 'cd')
|
|
|
|
self.assertEqual(2, conn.attempts)
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertEqual(next(resp), 'ef')
|
|
|
|
self.assertEqual(3, conn.attempts)
|
|
|
|
self.assertRaises(StopIteration, next, resp)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'range': 'bytes=2-',
|
|
|
|
'if-match': 'some etag',
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'range': 'bytes=4-',
|
|
|
|
'if-match': 'some etag',
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_chunk_size_iter_retry_bad_range_response(self):
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.url', 'tToken')
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(200, 'abcdef', {'etag': 'some etag',
|
|
|
|
'content-length': '6'}),
|
|
|
|
StubResponse(206, 'abcdef', {'etag': 'some etag',
|
|
|
|
'content-length': '6',
|
|
|
|
'content-range': 'chunk 1-2/3'})
|
|
|
|
)
|
|
|
|
__, resp = conn.get_object('asdf', 'asdf', resp_chunk_size=2)
|
|
|
|
self.assertEqual(next(resp), 'ab')
|
|
|
|
self.assertEqual(1, conn.attempts)
|
|
|
|
# simulate a dropped connection
|
|
|
|
resp.resp.read()
|
|
|
|
self.assertRaises(c.ClientException, next, resp)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
('GET', '/asdf/asdf', '', {
|
|
|
|
'range': 'bytes=2-',
|
|
|
|
'if-match': 'some etag',
|
|
|
|
'x-auth-token': 'tToken',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
2015-03-04 14:31:00 +00:00
|
|
|
def test_get_object_with_resp_chunk_size_zero(self):
|
|
|
|
def get_connection(self):
|
|
|
|
def get_auth():
|
|
|
|
return 'http://auth.test.com', 'token'
|
|
|
|
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
|
|
|
self.assertIs(type(conn), c.Connection)
|
|
|
|
conn.get_auth = get_auth
|
|
|
|
self.assertEqual(conn.attempts, 0)
|
|
|
|
return conn
|
|
|
|
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = get_connection(self)
|
|
|
|
conn.get_object('container1', 'obj1', resp_chunk_size=0)
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestHeadObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_server_error(self):
|
2016-01-18 17:05:28 -08:00
|
|
|
body = 'c' * 60
|
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.head_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-09-15 14:34:53 +05:30
|
|
|
def test_request_headers(self):
|
|
|
|
c.http_connection = self.fake_http_connection(204)
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
headers = {'x-client-key': 'client key'}
|
|
|
|
c.head_object('url_is_irrelevant', 'TOKEN', 'container',
|
|
|
|
'asdf', http_conn=conn, headers=headers)
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/container/asdf', '', {
|
|
|
|
'x-auth-token': 'TOKEN',
|
|
|
|
'x-client-key': 'client key',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
2018-03-05 17:33:22 -08:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(204)
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
query_string = 'foo=bar'
|
|
|
|
c.head_object('url_is_irrelevant', 'token', 'container', 'key',
|
|
|
|
http_conn=conn, query_string=query_string)
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/container/key?foo=bar', '', {'x-auth-token': 'token'})
|
|
|
|
])
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestPutObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
2015-09-23 09:55:10 +01:00
|
|
|
args = ('http://www.test.com', 'TOKEN', 'container', 'obj', 'body', 4)
|
2012-05-08 15:51:15 +01:00
|
|
|
value = c.put_object(*args)
|
2022-02-17 11:37:13 +00:00
|
|
|
self.assertIsInstance(value, str)
|
2015-09-23 09:55:10 +01:00
|
|
|
self.assertEqual(value, EMPTY_ETAG)
|
|
|
|
self.assertRequests([
|
|
|
|
('PUT', '/container/obj', 'body', {
|
|
|
|
'x-auth-token': 'TOKEN',
|
|
|
|
'content-length': '4',
|
|
|
|
}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2012-10-11 15:04:00 +03:00
|
|
|
def test_unicode_ok(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
|
|
|
mock_file = io.StringIO('\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91')
|
|
|
|
args = ('\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
2013-08-02 20:51:58 +08:00
|
|
|
mock_file)
|
2022-03-21 18:17:41 +00:00
|
|
|
text = '\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
2014-04-02 14:34:50 +02:00
|
|
|
headers = {'X-Header1': text,
|
2016-02-19 13:18:15 +00:00
|
|
|
'X-2': '1', 'X-3': "{'a': 'b'}", 'a-b': '.x:yz mn:fg:lp'}
|
2012-10-11 15:04:00 +03:00
|
|
|
|
|
|
|
resp = MockHttpResponse()
|
|
|
|
conn[1].getresponse = resp.fake_response
|
2014-01-24 17:40:16 +01:00
|
|
|
conn[1]._request = resp._fake_request
|
2012-10-11 15:04:00 +03:00
|
|
|
value = c.put_object(*args, headers=headers, http_conn=conn)
|
2022-02-17 11:37:13 +00:00
|
|
|
self.assertIsInstance(value, str)
|
2012-10-11 15:04:00 +03:00
|
|
|
# Test for RFC-2616 encoded symbols
|
2014-04-15 16:28:50 -04:00
|
|
|
self.assertIn(("a-b", b".x:yz mn:fg:lp"),
|
2014-04-02 14:34:50 +02:00
|
|
|
resp.buffer)
|
|
|
|
# Test unicode header
|
2014-04-15 16:28:50 -04:00
|
|
|
self.assertIn(('x-header1', text.encode('utf8')),
|
2014-04-02 14:34:50 +02:00
|
|
|
resp.buffer)
|
2012-10-11 15:04:00 +03:00
|
|
|
|
2013-03-05 16:56:02 +00:00
|
|
|
def test_chunk_warning(self):
|
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2022-02-17 11:37:13 +00:00
|
|
|
mock_file = io.StringIO('asdf')
|
2013-08-02 20:51:58 +08:00
|
|
|
args = ('asdf', 'asdf', 'asdf', 'asdf', mock_file)
|
2013-03-05 16:56:02 +00:00
|
|
|
resp = MockHttpResponse()
|
|
|
|
conn[1].getresponse = resp.fake_response
|
2014-01-24 17:40:16 +01:00
|
|
|
conn[1]._request = resp._fake_request
|
2013-03-05 16:56:02 +00:00
|
|
|
with warnings.catch_warnings(record=True) as w:
|
|
|
|
c.put_object(*args, chunk_size=20, headers={}, http_conn=conn)
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(len(w), 0)
|
2013-03-05 16:56:02 +00:00
|
|
|
|
|
|
|
body = 'c' * 60
|
|
|
|
c.http_connection = self.fake_http_connection(200, body=body)
|
|
|
|
args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
|
|
|
|
with warnings.catch_warnings(record=True) as w:
|
|
|
|
c.put_object(*args, chunk_size=20)
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(len(w), 1)
|
2013-03-05 16:56:02 +00:00
|
|
|
self.assertTrue(issubclass(w[-1].category, UserWarning))
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 60
|
2016-01-18 17:05:28 -08:00
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2012-05-08 15:51:15 +01:00
|
|
|
args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.put_object(*args)
|
|
|
|
e = exc_context.exception
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertEqual(e.http_response_content, body)
|
2016-01-18 17:05:28 -08:00
|
|
|
self.assertEqual(e.http_response_headers, headers)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertEqual(e.http_status, 500)
|
|
|
|
self.assertRequests([
|
2015-09-22 12:14:18 -07:00
|
|
|
('PUT', '/asdf/asdf', 'asdf', {
|
2022-02-10 18:37:09 +02:00
|
|
|
'x-auth-token': 'asdf'}),
|
2014-10-24 01:02:53 -07:00
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-03-05 15:12:04 -08:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200,
|
|
|
|
query_string="hello=20")
|
|
|
|
c.put_object('http://www.test.com', 'asdf', 'asdf', 'asdf',
|
|
|
|
query_string="hello=20")
|
2014-10-24 01:02:53 -07:00
|
|
|
for req in self.iter_request_log():
|
|
|
|
self.assertEqual(req['method'], 'PUT')
|
|
|
|
self.assertEqual(req['parsed_path'].path, '/asdf/asdf')
|
|
|
|
self.assertEqual(req['parsed_path'].query, 'hello=20')
|
|
|
|
self.assertEqual(req['headers']['x-auth-token'], 'asdf')
|
2013-03-05 15:12:04 -08:00
|
|
|
|
2014-02-18 11:28:04 +01:00
|
|
|
def test_raw_upload(self):
|
|
|
|
# Raw upload happens when content_length is passed to put_object
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2014-02-18 11:28:04 +01:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
2014-10-29 10:11:29 +00:00
|
|
|
raw_data = b'asdf' * 256
|
|
|
|
raw_data_len = len(raw_data)
|
2014-02-18 11:28:04 +01:00
|
|
|
|
2014-10-29 10:11:29 +00:00
|
|
|
for kwarg in ({'headers': {'Content-Length': str(raw_data_len)}},
|
|
|
|
{'content_length': raw_data_len}):
|
|
|
|
with tempfile.TemporaryFile() as mock_file:
|
|
|
|
mock_file.write(raw_data)
|
|
|
|
mock_file.seek(0)
|
2014-02-18 11:28:04 +01:00
|
|
|
|
2014-10-29 10:11:29 +00:00
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn,
|
|
|
|
contents=mock_file, **kwarg)
|
|
|
|
|
|
|
|
req_data = resp.requests_params['data']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIs(type(req_data), swiftclient.utils.LengthWrapper)
|
2014-10-29 10:11:29 +00:00
|
|
|
self.assertEqual(raw_data_len, len(req_data.read()))
|
2014-02-18 11:28:04 +01:00
|
|
|
|
|
|
|
def test_chunk_upload(self):
|
|
|
|
# Chunked upload happens when no content_length is passed to put_object
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2014-02-18 11:28:04 +01:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
2014-10-29 10:11:29 +00:00
|
|
|
raw_data = b'asdf' * 256
|
2014-02-18 11:28:04 +01:00
|
|
|
chunk_size = 16
|
|
|
|
|
2014-10-29 10:11:29 +00:00
|
|
|
with tempfile.TemporaryFile() as mock_file:
|
|
|
|
mock_file.write(raw_data)
|
|
|
|
mock_file.seek(0)
|
|
|
|
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn,
|
|
|
|
contents=mock_file, chunk_size=chunk_size)
|
|
|
|
req_data = resp.requests_params['data']
|
|
|
|
self.assertTrue(hasattr(req_data, '__iter__'))
|
|
|
|
data = b''
|
|
|
|
for chunk in req_data:
|
|
|
|
self.assertEqual(chunk_size, len(chunk))
|
|
|
|
data += chunk
|
|
|
|
self.assertEqual(data, raw_data)
|
|
|
|
|
2015-12-30 11:15:02 -08:00
|
|
|
def test_iter_upload(self):
|
|
|
|
def data():
|
|
|
|
for chunk in ('foo', '', 'bar'):
|
|
|
|
yield chunk
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2015-12-30 11:15:02 -08:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn,
|
|
|
|
contents=data())
|
|
|
|
req_headers = resp.requests_params['headers']
|
|
|
|
self.assertNotIn('Content-Length', req_headers)
|
|
|
|
req_data = resp.requests_params['data']
|
|
|
|
self.assertTrue(hasattr(req_data, '__iter__'))
|
|
|
|
# If we emit an empty chunk, requests will go ahead and send it,
|
|
|
|
# causing the server to close the connection. So make sure we don't
|
|
|
|
# do that.
|
|
|
|
self.assertEqual(['foo', 'bar'], list(req_data))
|
|
|
|
|
2014-10-29 10:11:29 +00:00
|
|
|
def test_md5_mismatch(self):
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
resp = MockHttpResponse(status=200, verify=True,
|
|
|
|
headers={'etag': '"badresponseetag"'})
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
raw_data = b'asdf' * 256
|
|
|
|
raw_data_md5 = md5(raw_data).hexdigest()
|
|
|
|
chunk_size = 16
|
|
|
|
|
|
|
|
with tempfile.TemporaryFile() as mock_file:
|
|
|
|
mock_file.write(raw_data)
|
|
|
|
mock_file.seek(0)
|
|
|
|
|
|
|
|
contents = swiftclient.utils.ReadableToIterable(mock_file,
|
|
|
|
md5=True)
|
|
|
|
|
|
|
|
etag = c.put_object(url='http://www.test.com',
|
|
|
|
http_conn=conn,
|
|
|
|
contents=contents,
|
|
|
|
chunk_size=chunk_size)
|
|
|
|
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertNotEqual(etag, contents.get_md5sum())
|
|
|
|
self.assertEqual(etag, 'badresponseetag')
|
|
|
|
self.assertEqual(raw_data_md5, contents.get_md5sum())
|
2014-10-29 10:11:29 +00:00
|
|
|
|
|
|
|
def test_md5_match(self):
|
|
|
|
conn = c.http_connection('http://www.test.com')
|
|
|
|
raw_data = b'asdf' * 256
|
|
|
|
raw_data_md5 = md5(raw_data).hexdigest()
|
|
|
|
resp = MockHttpResponse(status=200, verify=True,
|
|
|
|
headers={'etag': '"' + raw_data_md5 + '"'})
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
chunk_size = 16
|
|
|
|
|
|
|
|
with tempfile.TemporaryFile() as mock_file:
|
|
|
|
mock_file.write(raw_data)
|
|
|
|
mock_file.seek(0)
|
|
|
|
contents = swiftclient.utils.ReadableToIterable(mock_file,
|
|
|
|
md5=True)
|
|
|
|
|
|
|
|
etag = c.put_object(url='http://www.test.com',
|
|
|
|
http_conn=conn,
|
|
|
|
contents=contents,
|
|
|
|
chunk_size=chunk_size)
|
|
|
|
|
2015-08-03 12:20:44 +09:00
|
|
|
self.assertEqual(raw_data_md5, contents.get_md5sum())
|
|
|
|
self.assertEqual(etag, contents.get_md5sum())
|
2014-02-18 11:28:04 +01:00
|
|
|
|
|
|
|
def test_params(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2014-02-18 11:28:04 +01:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn,
|
|
|
|
etag='1234-5678', content_type='text/plain')
|
|
|
|
request_header = resp.requests_params['headers']
|
2014-05-22 16:56:31 +01:00
|
|
|
self.assertEqual(request_header['etag'], b'1234-5678')
|
|
|
|
self.assertEqual(request_header['content-type'], b'text/plain')
|
2014-02-18 11:28:04 +01:00
|
|
|
|
2022-03-21 18:17:41 +00:00
|
|
|
def test_no_content_type(self):
|
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2017-06-08 23:47:14 -07:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn)
|
|
|
|
request_header = resp.requests_params['headers']
|
|
|
|
self.assertNotIn('content-type', request_header)
|
|
|
|
|
2015-10-06 10:08:02 +02:00
|
|
|
def test_content_type_in_headers(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
2015-10-06 10:08:02 +02:00
|
|
|
resp = MockHttpResponse(status=200)
|
|
|
|
conn[1].getresponse = resp.fake_response
|
|
|
|
conn[1]._request = resp._fake_request
|
|
|
|
|
|
|
|
# title-case header
|
|
|
|
hdrs = {'Content-Type': 'text/Plain'}
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn, headers=hdrs)
|
|
|
|
request_header = resp.requests_params['headers']
|
|
|
|
self.assertEqual(request_header['content-type'], b'text/Plain')
|
|
|
|
|
|
|
|
# method param overrides headers
|
|
|
|
c.put_object(url='http://www.test.com', http_conn=conn, headers=hdrs,
|
|
|
|
content_type='image/jpeg')
|
|
|
|
request_header = resp.requests_params['headers']
|
|
|
|
self.assertEqual(request_header['content-type'], b'image/jpeg')
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
class TestPostObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
2016-02-26 11:25:10 -08:00
|
|
|
delete_at = 2.1 # not str! we don't know what other devs will use!
|
2015-09-23 09:55:10 +01:00
|
|
|
args = ('http://www.test.com', 'token', 'container', 'obj',
|
2016-02-26 11:25:10 -08:00
|
|
|
{'X-Object-Meta-Test': 'mymeta',
|
|
|
|
'X-Delete-At': delete_at})
|
2013-04-28 19:16:38 -07:00
|
|
|
c.post_object(*args)
|
2015-09-23 09:55:10 +01:00
|
|
|
self.assertRequests([
|
|
|
|
('POST', '/container/obj', '', {
|
|
|
|
'x-auth-token': 'token',
|
2016-02-26 11:25:10 -08:00
|
|
|
'X-Object-Meta-Test': 'mymeta',
|
|
|
|
'X-Delete-At': delete_at}),
|
2015-09-23 09:55:10 +01:00
|
|
|
])
|
2017-08-25 12:13:12 -07:00
|
|
|
# Check that the request header dict didn't get mutated
|
|
|
|
self.assertEqual(args[-1], {
|
|
|
|
'X-Object-Meta-Test': 'mymeta',
|
|
|
|
'X-Delete-At': delete_at,
|
|
|
|
})
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2012-10-11 15:04:00 +03:00
|
|
|
def test_unicode_ok(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/')
|
|
|
|
args = ('\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91')
|
|
|
|
text = '\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
2014-04-02 14:34:50 +02:00
|
|
|
headers = {'X-Header1': text,
|
2014-05-20 11:41:03 +00:00
|
|
|
b'X-Header2': 'value',
|
2016-02-19 13:18:15 +00:00
|
|
|
'X-2': '1', 'X-3': "{'a': 'b'}", 'a-b': '.x:yz mn:kl:qr',
|
2014-05-20 11:41:03 +00:00
|
|
|
'X-Object-Meta-Header-not-encoded': text,
|
|
|
|
b'X-Object-Meta-Header-encoded': 'value'}
|
2012-10-11 15:04:00 +03:00
|
|
|
|
|
|
|
resp = MockHttpResponse()
|
|
|
|
conn[1].getresponse = resp.fake_response
|
2014-01-24 17:40:16 +01:00
|
|
|
conn[1]._request = resp._fake_request
|
2012-10-11 15:04:00 +03:00
|
|
|
c.post_object(*args, headers=headers, http_conn=conn)
|
|
|
|
# Test for RFC-2616 encoded symbols
|
2014-04-15 16:28:50 -04:00
|
|
|
self.assertIn(('a-b', b".x:yz mn:kl:qr"), resp.buffer)
|
2014-04-02 14:34:50 +02:00
|
|
|
# Test unicode header
|
2014-04-15 16:28:50 -04:00
|
|
|
self.assertIn(('x-header1', text.encode('utf8')),
|
2014-04-02 14:34:50 +02:00
|
|
|
resp.buffer)
|
2014-05-20 11:41:03 +00:00
|
|
|
self.assertIn((b'x-object-meta-header-not-encoded',
|
|
|
|
text.encode('utf8')), resp.buffer)
|
|
|
|
self.assertIn((b'x-object-meta-header-encoded', b'value'),
|
|
|
|
resp.buffer)
|
|
|
|
self.assertIn((b'x-header2', b'value'), resp.buffer)
|
2012-10-11 15:04:00 +03:00
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_server_error(self):
|
|
|
|
body = 'c' * 60
|
2016-01-18 17:05:28 -08:00
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2014-10-24 01:02:53 -07:00
|
|
|
args = ('http://www.test.com', 'token', 'container', 'obj', {})
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.post_object(*args)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
2016-01-18 17:05:28 -08:00
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertRequests([
|
|
|
|
('POST', 'http://www.test.com/container/obj', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
|
2016-02-15 12:14:17 +01:00
|
|
|
class TestCopyObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_server_error(self):
|
|
|
|
c.http_connection = self.fake_http_connection(500)
|
|
|
|
self.assertRaises(
|
|
|
|
c.ClientException, c.copy_object,
|
|
|
|
'http://www.test.com/v1/AUTH', 'asdf', 'asdf', 'asdf')
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj',
|
|
|
|
destination='/container2/obj')
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'X-Auth-Token': 'token',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_service_token(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object('http://www.test.com/v1/AUTH', None, 'container',
|
|
|
|
'obj', destination='/container2/obj',
|
|
|
|
service_token="TOKEN")
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'X-Service-Token': 'TOKEN',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_headers(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj',
|
|
|
|
destination='/container2/obj',
|
|
|
|
headers={'some-hdr': 'a', 'other-hdr': 'b'})
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'X-Auth-Token': 'token',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
'some-hdr': 'a',
|
|
|
|
'other-hdr': 'b',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_fresh_metadata_default(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj',
|
|
|
|
'/container2/obj', {'x-fresh-metadata': 'hdr-value'})
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'X-Auth-Token': 'token',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
'X-Fresh-Metadata': 'hdr-value',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_fresh_metadata_true(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj',
|
|
|
|
destination='/container2/obj',
|
|
|
|
headers={'x-fresh-metadata': 'hdr-value'},
|
|
|
|
fresh_metadata=True)
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'X-Auth-Token': 'token',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
'X-Fresh-Metadata': 'true',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_fresh_metadata_false(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj',
|
|
|
|
destination='/container2/obj',
|
|
|
|
headers={'x-fresh-metadata': 'hdr-value'},
|
|
|
|
fresh_metadata=False)
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'Destination': '/container2/obj',
|
|
|
|
'X-Fresh-Metadata': 'false',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_no_destination(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
c.copy_object(
|
|
|
|
'http://www.test.com/v1/AUTH', 'token', 'container', 'obj')
|
|
|
|
self.assertRequests([
|
|
|
|
('COPY', 'http://www.test.com/v1/AUTH/container/obj', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'Destination': '/container/obj',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
class TestDeleteObject(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
2015-09-23 09:55:10 +01:00
|
|
|
c.delete_object('http://www.test.com', 'token', 'container', 'obj')
|
|
|
|
self.assertRequests([
|
|
|
|
('DELETE', 'http://www.test.com/container/obj', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
}),
|
|
|
|
])
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def test_server_error(self):
|
2016-01-18 17:05:28 -08:00
|
|
|
body = 'c' * 60
|
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.delete_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-03-05 15:12:04 -08:00
|
|
|
def test_query_string(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200,
|
|
|
|
query_string="hello=20")
|
2015-09-23 09:55:10 +01:00
|
|
|
c.delete_object('http://www.test.com', 'token', 'container', 'obj',
|
2013-03-05 15:12:04 -08:00
|
|
|
query_string="hello=20")
|
2015-09-23 09:55:10 +01:00
|
|
|
self.assertRequests([
|
|
|
|
('DELETE', 'http://www.test.com/container/obj?hello=20', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
}),
|
|
|
|
])
|
2013-03-05 15:12:04 -08:00
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2014-01-13 22:39:28 +01:00
|
|
|
class TestGetCapabilities(MockHttpTest):
|
|
|
|
|
|
|
|
def test_ok(self):
|
2015-05-14 17:09:43 -07:00
|
|
|
conn = self.fake_http_connection(200, body=b'{}')
|
2014-01-13 22:39:28 +01:00
|
|
|
http_conn = conn('http://www.test.com/info')
|
2014-10-24 01:02:53 -07:00
|
|
|
info = c.get_capabilities(http_conn)
|
|
|
|
self.assertRequests([
|
2015-05-21 22:44:36 -07:00
|
|
|
('GET', '/info', '', {'Accept-Encoding': 'gzip'}),
|
2014-10-24 01:02:53 -07:00
|
|
|
])
|
|
|
|
self.assertEqual(info, {})
|
|
|
|
self.assertTrue(http_conn[1].resp.has_been_read)
|
2014-01-13 22:39:28 +01:00
|
|
|
|
|
|
|
def test_server_error(self):
|
2016-01-18 17:05:28 -08:00
|
|
|
body = 'c' * 60
|
|
|
|
headers = {'foo': 'bar'}
|
|
|
|
conn = self.fake_http_connection(
|
|
|
|
StubResponse(500, body, headers))
|
2014-01-13 22:39:28 +01:00
|
|
|
http_conn = conn('http://www.test.com/info')
|
2016-01-18 17:05:28 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.get_capabilities(http_conn)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_content, body)
|
|
|
|
self.assertEqual(exc_context.exception.http_response_headers, headers)
|
2014-01-13 22:39:28 +01:00
|
|
|
|
2014-10-24 01:02:53 -07:00
|
|
|
def test_conn_get_capabilities_with_auth(self):
|
|
|
|
auth_headers = {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'x-storage-url': 'http://storage.example.com/v1/AUTH_test'
|
|
|
|
}
|
|
|
|
auth_v1_response = StubResponse(headers=auth_headers)
|
|
|
|
stub_info = {'swift': {'fake': True}}
|
2015-05-14 17:09:43 -07:00
|
|
|
info_response = StubResponse(body=b'{"swift":{"fake":true}}')
|
2014-10-24 01:02:53 -07:00
|
|
|
fake_conn = self.fake_http_connection(auth_v1_response, info_response)
|
|
|
|
|
|
|
|
conn = c.Connection('http://auth.example.com/auth/v1.0',
|
|
|
|
'user', 'key')
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
new=fake_conn):
|
|
|
|
info = conn.get_capabilities()
|
|
|
|
self.assertEqual(info, stub_info)
|
|
|
|
self.assertRequests([
|
2015-09-22 12:14:18 -07:00
|
|
|
('GET', '/auth/v1.0', '', {
|
|
|
|
'x-auth-user': 'user',
|
|
|
|
'x-auth-key': 'key'}),
|
2015-05-21 22:44:36 -07:00
|
|
|
('GET', 'http://storage.example.com/info', '', {
|
|
|
|
'accept-encoding': 'gzip'}),
|
2014-10-24 01:02:53 -07:00
|
|
|
])
|
|
|
|
|
|
|
|
def test_conn_get_capabilities_with_os_auth(self):
|
|
|
|
fake_keystone = fake_get_auth_keystone(
|
|
|
|
storage_url='http://storage.example.com/v1/AUTH_test')
|
|
|
|
stub_info = {'swift': {'fake': True}}
|
2015-05-14 17:09:43 -07:00
|
|
|
info_response = StubResponse(body=b'{"swift":{"fake":true}}')
|
2014-10-24 01:02:53 -07:00
|
|
|
fake_conn = self.fake_http_connection(info_response)
|
|
|
|
|
|
|
|
os_options = {'project_id': 'test'}
|
|
|
|
conn = c.Connection('http://keystone.example.com/v3.0',
|
|
|
|
'user', 'key', os_options=os_options,
|
|
|
|
auth_version=3)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
get_auth_keystone=fake_keystone,
|
|
|
|
http_connection=fake_conn):
|
|
|
|
info = conn.get_capabilities()
|
|
|
|
self.assertEqual(info, stub_info)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://storage.example.com/info'),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_conn_get_capabilities_with_url_param(self):
|
|
|
|
stub_info = {'swift': {'fake': True}}
|
2015-05-14 17:09:43 -07:00
|
|
|
info_response = StubResponse(body=b'{"swift":{"fake":true}}')
|
2014-10-24 01:02:53 -07:00
|
|
|
fake_conn = self.fake_http_connection(info_response)
|
|
|
|
|
|
|
|
conn = c.Connection('http://auth.example.com/auth/v1.0',
|
|
|
|
'user', 'key')
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
new=fake_conn):
|
|
|
|
info = conn.get_capabilities(
|
|
|
|
'http://other-storage.example.com/info')
|
|
|
|
self.assertEqual(info, stub_info)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://other-storage.example.com/info'),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_conn_get_capabilities_with_preauthurl_param(self):
|
|
|
|
stub_info = {'swift': {'fake': True}}
|
2015-05-14 17:09:43 -07:00
|
|
|
info_response = StubResponse(body=b'{"swift":{"fake":true}}')
|
2014-10-24 01:02:53 -07:00
|
|
|
fake_conn = self.fake_http_connection(info_response)
|
|
|
|
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_test'
|
|
|
|
conn = c.Connection('http://auth.example.com/auth/v1.0',
|
|
|
|
'user', 'key', preauthurl=storage_url)
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
new=fake_conn):
|
|
|
|
info = conn.get_capabilities()
|
|
|
|
self.assertEqual(info, stub_info)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://storage.example.com/info'),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_conn_get_capabilities_with_os_options(self):
|
|
|
|
stub_info = {'swift': {'fake': True}}
|
2015-05-14 17:09:43 -07:00
|
|
|
info_response = StubResponse(body=b'{"swift":{"fake":true}}')
|
2014-10-24 01:02:53 -07:00
|
|
|
fake_conn = self.fake_http_connection(info_response)
|
|
|
|
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_test'
|
|
|
|
os_options = {
|
|
|
|
'project_id': 'test',
|
|
|
|
'object_storage_url': storage_url,
|
|
|
|
}
|
|
|
|
conn = c.Connection('http://keystone.example.com/v3.0',
|
|
|
|
'user', 'key', os_options=os_options,
|
|
|
|
auth_version=3)
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
new=fake_conn):
|
|
|
|
info = conn.get_capabilities()
|
|
|
|
self.assertEqual(info, stub_info)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://storage.example.com/info'),
|
|
|
|
])
|
|
|
|
|
2014-01-13 22:39:28 +01:00
|
|
|
|
2014-02-18 11:28:04 +01:00
|
|
|
class TestHTTPConnection(MockHttpTest):
|
|
|
|
|
2015-02-23 11:09:39 +00:00
|
|
|
def test_bad_url_scheme(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
url = 'www.test.com'
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.http_connection(url)
|
|
|
|
exc = exc_context.exception
|
2022-03-21 18:17:41 +00:00
|
|
|
expected = 'Unsupported scheme "" in url "www.test.com"'
|
2015-02-23 11:09:39 +00:00
|
|
|
self.assertEqual(expected, str(exc))
|
|
|
|
|
2022-03-21 18:17:41 +00:00
|
|
|
url = '://www.test.com'
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.http_connection(url)
|
|
|
|
exc = exc_context.exception
|
2022-03-21 18:17:41 +00:00
|
|
|
expected = 'Unsupported scheme "" in url "://www.test.com"'
|
2015-02-23 11:09:39 +00:00
|
|
|
self.assertEqual(expected, str(exc))
|
|
|
|
|
2022-03-21 18:17:41 +00:00
|
|
|
url = 'blah://www.test.com'
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.http_connection(url)
|
|
|
|
exc = exc_context.exception
|
2022-03-21 18:17:41 +00:00
|
|
|
expected = 'Unsupported scheme "blah" in url "blah://www.test.com"'
|
2015-02-23 11:09:39 +00:00
|
|
|
self.assertEqual(expected, str(exc))
|
|
|
|
|
|
|
|
def test_ok_url_scheme(self):
|
|
|
|
for scheme in ('http', 'https', 'HTTP', 'HTTPS'):
|
2022-03-21 18:17:41 +00:00
|
|
|
url = '%s://www.test.com' % scheme
|
2015-02-23 11:09:39 +00:00
|
|
|
parsed_url, conn = c.http_connection(url)
|
|
|
|
self.assertEqual(scheme.lower(), parsed_url.scheme)
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('%s://www.test.com' % scheme, conn.url)
|
2015-02-23 11:09:39 +00:00
|
|
|
|
2014-02-18 11:28:04 +01:00
|
|
|
def test_ok_proxy(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/',
|
2014-02-18 11:28:04 +01:00
|
|
|
proxy='http://localhost:8080')
|
2014-05-14 19:08:31 +02:00
|
|
|
self.assertEqual(conn[1].requests_args['proxies']['http'],
|
|
|
|
'http://localhost:8080')
|
2014-02-18 11:28:04 +01:00
|
|
|
|
|
|
|
def test_bad_proxy(self):
|
|
|
|
try:
|
2022-03-21 18:17:41 +00:00
|
|
|
c.http_connection('http://www.test.com/', proxy='localhost:8080')
|
2014-02-18 11:28:04 +01:00
|
|
|
except c.ClientException as e:
|
|
|
|
self.assertEqual(e.msg, "Proxy's missing scheme")
|
|
|
|
|
|
|
|
def test_cacert(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/',
|
2014-02-18 11:28:04 +01:00
|
|
|
cacert='/dev/urandom')
|
2014-05-14 19:08:31 +02:00
|
|
|
self.assertEqual(conn[1].requests_args['verify'], '/dev/urandom')
|
2014-02-18 11:28:04 +01:00
|
|
|
|
|
|
|
def test_insecure(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/', insecure=True)
|
2014-05-14 19:08:31 +02:00
|
|
|
self.assertEqual(conn[1].requests_args['verify'], False)
|
2014-02-18 11:28:04 +01:00
|
|
|
|
2016-04-10 23:18:17 +02:00
|
|
|
def test_cert(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
conn = c.http_connection('http://www.test.com/', cert='minnie')
|
2016-04-10 23:18:17 +02:00
|
|
|
self.assertEqual(conn[1].requests_args['cert'], 'minnie')
|
|
|
|
|
|
|
|
def test_cert_key(self):
|
|
|
|
conn = c.http_connection(
|
2022-03-21 18:17:41 +00:00
|
|
|
'http://www.test.com/', cert='minnie', cert_key='mickey')
|
2016-04-10 23:18:17 +02:00
|
|
|
self.assertEqual(conn[1].requests_args['cert'], ('minnie', 'mickey'))
|
|
|
|
|
2015-02-25 10:58:27 +00:00
|
|
|
def test_response_connection_released(self):
|
2022-03-21 18:17:41 +00:00
|
|
|
_parsed_url, conn = c.http_connection('http://www.test.com/')
|
2015-02-25 10:58:27 +00:00
|
|
|
conn.resp = MockHttpResponse()
|
|
|
|
conn.resp.raw = mock.Mock()
|
|
|
|
conn.resp.raw.read.side_effect = ["Chunk", ""]
|
|
|
|
resp = conn.getresponse()
|
|
|
|
self.assertFalse(resp.closed)
|
|
|
|
self.assertEqual("Chunk", resp.read())
|
|
|
|
self.assertFalse(resp.read())
|
|
|
|
self.assertTrue(resp.closed)
|
|
|
|
|
2022-02-17 11:37:13 +00:00
|
|
|
def test_response_headers(self):
|
2022-02-17 11:21:40 +00:00
|
|
|
'''Test latin1-encoded headers.
|
2018-04-17 14:36:57 -07:00
|
|
|
'''
|
2022-03-21 18:17:41 +00:00
|
|
|
_, conn = c.http_connection('http://www.test.com/')
|
2018-04-17 14:36:57 -07:00
|
|
|
conn.resp = MockHttpResponse(
|
|
|
|
status=200,
|
|
|
|
headers={
|
|
|
|
b'\xd8\xaa-unicode'.decode('iso-8859-1'):
|
|
|
|
b'\xd8\xaa-value'.decode('iso-8859-1'),
|
|
|
|
'empty-header': ''
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
resp = conn.getresponse()
|
|
|
|
self.assertEqual(
|
|
|
|
'\u062a-value', resp.getheader('\u062a-unicode'))
|
|
|
|
self.assertEqual(
|
|
|
|
'\u062a-value', resp.getheader('\u062a-UNICODE'))
|
|
|
|
self.assertEqual('', resp.getheader('empty-header'))
|
|
|
|
self.assertEqual(
|
|
|
|
dict([('\u062a-unicode', '\u062a-value'),
|
|
|
|
('empty-header', ''),
|
|
|
|
('etag', ('"%s"' % EMPTY_ETAG))]),
|
|
|
|
dict(resp.getheaders()))
|
|
|
|
|
2014-02-18 11:28:04 +01:00
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
class TestConnection(MockHttpTest):
|
|
|
|
|
|
|
|
def test_instance(self):
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(conn.retries, 5)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2012-10-23 09:18:29 +01:00
|
|
|
def test_instance_kwargs(self):
|
2013-04-28 19:16:38 -07:00
|
|
|
args = {'user': 'ausername',
|
|
|
|
'key': 'secretpass',
|
|
|
|
'authurl': 'http://www.test.com',
|
|
|
|
'tenant_name': 'atenant'}
|
2012-10-23 09:18:29 +01:00
|
|
|
conn = c.Connection(**args)
|
2024-02-20 16:21:50 +11:00
|
|
|
self.assertIsInstance(conn, c.Connection)
|
2012-10-23 09:18:29 +01:00
|
|
|
|
|
|
|
def test_instance_kwargs_token(self):
|
|
|
|
args = {'preauthtoken': 'atoken123',
|
2013-04-28 19:16:38 -07:00
|
|
|
'preauthurl': 'http://www.test.com:8080/v1/AUTH_123456'}
|
2012-10-23 09:18:29 +01:00
|
|
|
conn = c.Connection(**args)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertEqual(conn.url, args['preauthurl'])
|
|
|
|
self.assertEqual(conn.token, args['preauthtoken'])
|
|
|
|
|
|
|
|
def test_instance_kwargs_os_token(self):
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_test'
|
|
|
|
token = 'token'
|
|
|
|
args = {
|
|
|
|
'os_options': {
|
|
|
|
'object_storage_url': storage_url,
|
|
|
|
'auth_token': token,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
conn = c.Connection(**args)
|
|
|
|
self.assertEqual(conn.url, storage_url)
|
|
|
|
self.assertEqual(conn.token, token)
|
|
|
|
|
|
|
|
def test_instance_kwargs_token_precedence(self):
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_test'
|
|
|
|
token = 'token'
|
|
|
|
args = {
|
|
|
|
'preauthurl': storage_url,
|
|
|
|
'preauthtoken': token,
|
|
|
|
'os_options': {
|
|
|
|
'auth_token': 'less-specific-token',
|
|
|
|
'object_storage_url': 'less-specific-storage-url',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
conn = c.Connection(**args)
|
|
|
|
self.assertEqual(conn.url, storage_url)
|
|
|
|
self.assertEqual(conn.token, token)
|
2012-10-23 09:18:29 +01:00
|
|
|
|
2013-06-28 21:26:54 -07:00
|
|
|
def test_storage_url_override(self):
|
|
|
|
static_url = 'http://overridden.storage.url'
|
|
|
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key',
|
|
|
|
os_options={
|
|
|
|
'object_storage_url': static_url})
|
|
|
|
method_signatures = (
|
|
|
|
(conn.head_account, []),
|
|
|
|
(conn.get_account, []),
|
|
|
|
(conn.head_container, ('asdf',)),
|
|
|
|
(conn.get_container, ('asdf',)),
|
|
|
|
(conn.put_container, ('asdf',)),
|
|
|
|
(conn.delete_container, ('asdf',)),
|
|
|
|
(conn.head_object, ('asdf', 'asdf')),
|
|
|
|
(conn.get_object, ('asdf', 'asdf')),
|
|
|
|
(conn.put_object, ('asdf', 'asdf', 'asdf')),
|
|
|
|
(conn.post_object, ('asdf', 'asdf', {})),
|
|
|
|
(conn.delete_object, ('asdf', 'asdf')),
|
|
|
|
)
|
|
|
|
|
|
|
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = ('http://auth.storage.url', 'tToken')
|
|
|
|
|
|
|
|
for method, args in method_signatures:
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(
|
2015-05-14 17:09:43 -07:00
|
|
|
200, body=b'[]', storage_url=static_url)
|
2013-06-28 21:26:54 -07:00
|
|
|
method(*args)
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertEqual(len(self.request_log), 1)
|
|
|
|
for request in self.iter_request_log():
|
|
|
|
self.assertEqual(request['parsed_path'].netloc,
|
|
|
|
'overridden.storage.url')
|
|
|
|
self.assertEqual(request['headers']['x-auth-token'],
|
|
|
|
'tToken')
|
2013-06-28 21:26:54 -07:00
|
|
|
|
2020-04-23 19:01:47 +03:00
|
|
|
def test_url_mapping(self):
|
|
|
|
conn = c.Connection()
|
|
|
|
uri_versions = {
|
|
|
|
'http://storage.test.com':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/v1':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/v1/':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/swift':
|
|
|
|
'http://storage.test.com/swift/info',
|
|
|
|
'http://storage.test.com/swift/':
|
|
|
|
'http://storage.test.com/swift/info',
|
|
|
|
'http://storage.test.com/v1.0':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/swift/v1.0':
|
|
|
|
'http://storage.test.com/swift/info',
|
|
|
|
'http://storage.test.com/v111':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/v111/test':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/v1/test':
|
|
|
|
'http://storage.test.com/info',
|
|
|
|
'http://storage.test.com/swift/v1.0/test':
|
|
|
|
'http://storage.test.com/swift/info',
|
|
|
|
'http://storage.test.com/v1.0/test':
|
|
|
|
'http://storage.test.com/info'}
|
|
|
|
for uri_k, uri_v in uri_versions.items():
|
|
|
|
self.assertEqual(conn._map_url(uri_k), uri_v)
|
|
|
|
|
2014-01-13 22:39:28 +01:00
|
|
|
def test_get_capabilities(self):
|
|
|
|
conn = c.Connection()
|
|
|
|
with mock.patch('swiftclient.client.get_capabilities') as get_cap:
|
|
|
|
conn.get_capabilities('http://storage2.test.com')
|
|
|
|
parsed = get_cap.call_args[0][0][0]
|
|
|
|
self.assertEqual(parsed.path, '/info')
|
|
|
|
self.assertEqual(parsed.netloc, 'storage2.test.com')
|
|
|
|
conn.get_auth = lambda: ('http://storage.test.com/v1/AUTH_test',
|
|
|
|
'token')
|
|
|
|
conn.get_capabilities()
|
|
|
|
parsed = get_cap.call_args[0][0][0]
|
|
|
|
self.assertEqual(parsed.path, '/info')
|
|
|
|
self.assertEqual(parsed.netloc, 'storage.test.com')
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_retry(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
2014-10-24 01:02:53 -07:00
|
|
|
code_iter = [500] * (conn.retries + 1)
|
|
|
|
c.http_connection = self.fake_http_connection(*code_iter)
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
self.assertRaises(c.ClientException, conn.head_account)
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(conn.attempts, conn.retries + 1)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-12-23 13:49:46 -08:00
|
|
|
def test_retry_on_ratelimit(self):
|
|
|
|
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
|
2022-08-10 12:38:54 -05:00
|
|
|
def test_status_code(code):
|
|
|
|
# test retries
|
|
|
|
conn = c.Connection('http://www.test.com/auth/v1.0',
|
|
|
|
'asdf', 'asdf', retry_on_ratelimit=True)
|
|
|
|
code_iter = [200] + [code] * (conn.retries + 1)
|
|
|
|
auth_resp_headers = {
|
|
|
|
'x-auth-token': 'asdf',
|
|
|
|
'x-storage-url': 'http://storage/v1/test',
|
|
|
|
}
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
*code_iter, headers=auth_resp_headers)
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
conn.head_account()
|
|
|
|
self.assertIn('Account HEAD failed', str(exc_context.exception))
|
|
|
|
self.assertEqual(code, exc_context.exception.http_status)
|
|
|
|
self.assertEqual(conn.attempts, conn.retries + 1)
|
|
|
|
|
|
|
|
# test default no-retry
|
|
|
|
c.http_connection = self.fake_http_connection(
|
|
|
|
200, code,
|
|
|
|
headers=auth_resp_headers)
|
|
|
|
conn = c.Connection('http://www.test.com/auth/v1.0',
|
|
|
|
'asdf', 'asdf', retry_on_ratelimit=False)
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
conn.head_account()
|
|
|
|
self.assertIn('Account HEAD failed', str(exc_context.exception))
|
|
|
|
self.assertEqual(code, exc_context.exception.http_status)
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
2013-12-23 13:49:46 -08:00
|
|
|
|
2022-08-10 12:38:54 -05:00
|
|
|
test_status_code(498)
|
|
|
|
test_status_code(429)
|
2013-12-23 13:49:46 -08:00
|
|
|
|
Add force auth retry mode in swiftclient
This patch attemps to add an option to force get_auth call while retrying
an operation even if it gets errors other than 401 Unauthorized.
Why we need this:
The main reason why we need this is current python-swiftclient requests could
never get succeeded under certion situation using third party proxies/load balancers
between the client and swift-proxy server. I think, it would be general situation
of the use case.
Specifically describing nginx case, the nginx can close the socket from the client
when the response code from swift is not 2xx series. In default, nginx can wait the
buffers from the client for a while (default 30s)[1] but after the time past, nginx
will close the socket immediately. Unfortunately, if python-swiftclient has still been
sending the data into the socket, python-swiftclient will get socket error (EPIPE,
BrokenPipe). From the swiftclient perspective, this is absolutely not an auth error,
so current python-swiftclient will continue to retry without re-auth.
However, if the root cause is sort of 401 (i.e. nginx got 401 unauthorized from the
swift-proxy because of token expiration), swiftclient will loop 401 -> EPIPE -> 401...
until it consume the max retry times.
In particlar, less time to live of the token and multipart object upload with large
segments could not get succeeded as below:
Connection Model:
python-swiftclient -> nginx -> swift-proxy -> swift-backend
Case: Try to create slo with large segments and the auth token expired with 1 hour
1. client create a connection to nginx with successful response from swift-proxy and its auth
2. client continue to put large segment objects
(e.g. 1~5GB for each and the total would 20~30GB, i.e. 20~30 segments)
3. after some of segments uploaded, 1 hour past but client is still trying to
send remaining segment objects.
4. nginx got 401 from swift-proxy for a request and wait that the connection is closed
from the client but timeout past because the python-swiftclient is still sending much data
into the socket before reading the 401 response.
5. client got socket error because nginx closed the connection during sending the buffer.
6. client retries a new connection to nginx without re-auth...
<loop 4-6>
7. finally python-swiftclient failed with socket error (Broken Pipe)
In operational perspective, setting longer timeout for lingering close would be an option but
it's not complete solution because any other proxy/LB may not support the options.
If we actually do THE RIGHT THING in python-swiftclient, we should send expects: 100-continue
header and handle the first response to re-auth correctly.
HOWEVER, the current python's httplib and requests module used by python-swiftclient doesn't
support expects: 100-continue header [2] and the thread proposed a fix [3] is not super active.
And we know the reason we depends on the library is to fix a security issue that existed
in older python-swiftclient [4] so that we should touch around it super carefully.
In the reality, as the hot fix, this patch try to mitigate the unfortunate situation
described above WITHOUT 100-continue fix, just users can force to re-auth when any errors
occurred during the retries that can be accepted in the upstream.
1: http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close
2: https://github.com/requests/requests/issues/713
3: https://bugs.python.org/issue1346874
4: https://review.openstack.org/#/c/69187/
Change-Id: I3470b56e3f9cf9cdb8c2fc2a94b2c551927a3440
2018-03-12 17:54:17 +09:00
|
|
|
def test_retry_with_socket_error(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
|
|
|
with mock.patch('swiftclient.client.http_connection') as \
|
|
|
|
fake_http_connection, \
|
|
|
|
mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
|
|
|
|
mock_auth.return_value = ('http://mock.com', 'mock_token')
|
|
|
|
fake_http_connection.side_effect = socket.error
|
|
|
|
self.assertRaises(socket.error, conn.head_account)
|
|
|
|
self.assertEqual(mock_auth.call_count, 1)
|
|
|
|
self.assertEqual(conn.attempts, conn.retries + 1)
|
|
|
|
|
2023-03-17 16:14:15 -07:00
|
|
|
def test_no_retry_with_cert_sslerror(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
for err in (
|
|
|
|
# Taken from real testing (requests==2.25.1, urllib3==1.26.5,
|
|
|
|
# pyOpenSSL==21.0.0) but note that these are actually way more
|
|
|
|
# messy/wrapped up in other exceptions
|
|
|
|
SSLError(
|
|
|
|
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
|
|
|
|
'certificate has expired (_ssl.c:997)'),
|
|
|
|
SSLError(
|
|
|
|
"hostname 'wrong.host.badssl.com' doesn't match either of "
|
|
|
|
"'*.badssl.com', 'badssl.com'"),
|
|
|
|
SSLError(
|
|
|
|
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
|
|
|
|
'self-signed certificate (_ssl.c:997)'),
|
|
|
|
SSLError(
|
|
|
|
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
|
|
|
|
'self-signed certificate in certificate chain (_ssl.c:997)'),
|
|
|
|
SSLError(
|
|
|
|
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
|
|
|
|
'unable to get local issuer certificate (_ssl.c:997)'),
|
|
|
|
SSLError(
|
|
|
|
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
|
|
|
|
'CA signature digest algorithm too weak (_ssl.c:997)'),
|
|
|
|
):
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
|
|
|
with self.subTest(err=err), mock.patch(
|
|
|
|
'swiftclient.client.http_connection') as \
|
|
|
|
fake_http_connection, \
|
|
|
|
mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
|
|
|
|
mock_auth.return_value = ('http://mock.com', 'mock_token')
|
|
|
|
fake_http_connection.side_effect = err
|
|
|
|
self.assertRaises(socket.error, conn.head_account)
|
|
|
|
self.assertEqual(mock_auth.call_count, 1)
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_retry_with_non_cert_sslerror(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
|
|
|
with mock.patch('swiftclient.client.http_connection') as \
|
|
|
|
fake_http_connection, \
|
|
|
|
mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
|
|
|
|
mock_auth.return_value = ('http://mock.com', 'mock_token')
|
|
|
|
fake_http_connection.side_effect = SSLError(
|
|
|
|
"HTTPSConnectionPool(host='example.com', port=443): "
|
|
|
|
"Max retries exceeded with url: /v1/AUTH_test (Caused by "
|
|
|
|
"SSLError(SSLZeroReturnError(6, 'TLS/SSL connection has "
|
|
|
|
"been closed (EOF) (_ssl.c:997)')))")
|
|
|
|
self.assertRaises(socket.error, conn.head_account)
|
|
|
|
self.assertEqual(mock_auth.call_count, 1)
|
|
|
|
self.assertEqual(conn.attempts, conn.retries + 1)
|
|
|
|
|
Add force auth retry mode in swiftclient
This patch attemps to add an option to force get_auth call while retrying
an operation even if it gets errors other than 401 Unauthorized.
Why we need this:
The main reason why we need this is current python-swiftclient requests could
never get succeeded under certion situation using third party proxies/load balancers
between the client and swift-proxy server. I think, it would be general situation
of the use case.
Specifically describing nginx case, the nginx can close the socket from the client
when the response code from swift is not 2xx series. In default, nginx can wait the
buffers from the client for a while (default 30s)[1] but after the time past, nginx
will close the socket immediately. Unfortunately, if python-swiftclient has still been
sending the data into the socket, python-swiftclient will get socket error (EPIPE,
BrokenPipe). From the swiftclient perspective, this is absolutely not an auth error,
so current python-swiftclient will continue to retry without re-auth.
However, if the root cause is sort of 401 (i.e. nginx got 401 unauthorized from the
swift-proxy because of token expiration), swiftclient will loop 401 -> EPIPE -> 401...
until it consume the max retry times.
In particlar, less time to live of the token and multipart object upload with large
segments could not get succeeded as below:
Connection Model:
python-swiftclient -> nginx -> swift-proxy -> swift-backend
Case: Try to create slo with large segments and the auth token expired with 1 hour
1. client create a connection to nginx with successful response from swift-proxy and its auth
2. client continue to put large segment objects
(e.g. 1~5GB for each and the total would 20~30GB, i.e. 20~30 segments)
3. after some of segments uploaded, 1 hour past but client is still trying to
send remaining segment objects.
4. nginx got 401 from swift-proxy for a request and wait that the connection is closed
from the client but timeout past because the python-swiftclient is still sending much data
into the socket before reading the 401 response.
5. client got socket error because nginx closed the connection during sending the buffer.
6. client retries a new connection to nginx without re-auth...
<loop 4-6>
7. finally python-swiftclient failed with socket error (Broken Pipe)
In operational perspective, setting longer timeout for lingering close would be an option but
it's not complete solution because any other proxy/LB may not support the options.
If we actually do THE RIGHT THING in python-swiftclient, we should send expects: 100-continue
header and handle the first response to re-auth correctly.
HOWEVER, the current python's httplib and requests module used by python-swiftclient doesn't
support expects: 100-continue header [2] and the thread proposed a fix [3] is not super active.
And we know the reason we depends on the library is to fix a security issue that existed
in older python-swiftclient [4] so that we should touch around it super carefully.
In the reality, as the hot fix, this patch try to mitigate the unfortunate situation
described above WITHOUT 100-continue fix, just users can force to re-auth when any errors
occurred during the retries that can be accepted in the upstream.
1: http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close
2: https://github.com/requests/requests/issues/713
3: https://bugs.python.org/issue1346874
4: https://review.openstack.org/#/c/69187/
Change-Id: I3470b56e3f9cf9cdb8c2fc2a94b2c551927a3440
2018-03-12 17:54:17 +09:00
|
|
|
def test_retry_with_force_auth_retry_exceptions(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def do_test(exception):
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
conn = c.Connection(
|
|
|
|
'http://www.test.com', 'asdf', 'asdf',
|
|
|
|
force_auth_retry=True)
|
|
|
|
with mock.patch('swiftclient.client.http_connection') as \
|
|
|
|
fake_http_connection, \
|
|
|
|
mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
|
|
|
|
mock_auth.return_value = ('http://mock.com', 'mock_token')
|
|
|
|
fake_http_connection.side_effect = exception
|
|
|
|
self.assertRaises(exception, conn.head_account)
|
|
|
|
self.assertEqual(mock_auth.call_count, conn.retries + 1)
|
|
|
|
self.assertEqual(conn.attempts, conn.retries + 1)
|
|
|
|
|
|
|
|
do_test(socket.error)
|
|
|
|
do_test(RequestException)
|
|
|
|
|
|
|
|
def test_retry_with_force_auth_retry_client_exceptions(self):
|
|
|
|
def quick_sleep(*args):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def do_test(http_status, count):
|
|
|
|
|
|
|
|
def mock_http_connection(*args, **kwargs):
|
|
|
|
raise ClientException('fake', http_status=http_status)
|
|
|
|
|
|
|
|
c.sleep = quick_sleep
|
|
|
|
conn = c.Connection(
|
|
|
|
'http://www.test.com', 'asdf', 'asdf',
|
|
|
|
force_auth_retry=True)
|
|
|
|
with mock.patch('swiftclient.client.http_connection') as \
|
|
|
|
fake_http_connection, \
|
|
|
|
mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
|
|
|
|
mock_auth.return_value = ('http://mock.com', 'mock_token')
|
|
|
|
fake_http_connection.side_effect = mock_http_connection
|
|
|
|
self.assertRaises(ClientException, conn.head_account)
|
|
|
|
self.assertEqual(mock_auth.call_count, count)
|
|
|
|
self.assertEqual(conn.attempts, count)
|
|
|
|
|
|
|
|
# sanity, in case of 401, the auth will be called only twice because of
|
|
|
|
# retried_auth mechanism
|
|
|
|
do_test(401, 2)
|
|
|
|
# others will be tried until retry limits
|
|
|
|
do_test(408, 6)
|
2023-02-15 21:00:51 -08:00
|
|
|
do_test(499, 6)
|
Add force auth retry mode in swiftclient
This patch attemps to add an option to force get_auth call while retrying
an operation even if it gets errors other than 401 Unauthorized.
Why we need this:
The main reason why we need this is current python-swiftclient requests could
never get succeeded under certion situation using third party proxies/load balancers
between the client and swift-proxy server. I think, it would be general situation
of the use case.
Specifically describing nginx case, the nginx can close the socket from the client
when the response code from swift is not 2xx series. In default, nginx can wait the
buffers from the client for a while (default 30s)[1] but after the time past, nginx
will close the socket immediately. Unfortunately, if python-swiftclient has still been
sending the data into the socket, python-swiftclient will get socket error (EPIPE,
BrokenPipe). From the swiftclient perspective, this is absolutely not an auth error,
so current python-swiftclient will continue to retry without re-auth.
However, if the root cause is sort of 401 (i.e. nginx got 401 unauthorized from the
swift-proxy because of token expiration), swiftclient will loop 401 -> EPIPE -> 401...
until it consume the max retry times.
In particlar, less time to live of the token and multipart object upload with large
segments could not get succeeded as below:
Connection Model:
python-swiftclient -> nginx -> swift-proxy -> swift-backend
Case: Try to create slo with large segments and the auth token expired with 1 hour
1. client create a connection to nginx with successful response from swift-proxy and its auth
2. client continue to put large segment objects
(e.g. 1~5GB for each and the total would 20~30GB, i.e. 20~30 segments)
3. after some of segments uploaded, 1 hour past but client is still trying to
send remaining segment objects.
4. nginx got 401 from swift-proxy for a request and wait that the connection is closed
from the client but timeout past because the python-swiftclient is still sending much data
into the socket before reading the 401 response.
5. client got socket error because nginx closed the connection during sending the buffer.
6. client retries a new connection to nginx without re-auth...
<loop 4-6>
7. finally python-swiftclient failed with socket error (Broken Pipe)
In operational perspective, setting longer timeout for lingering close would be an option but
it's not complete solution because any other proxy/LB may not support the options.
If we actually do THE RIGHT THING in python-swiftclient, we should send expects: 100-continue
header and handle the first response to re-auth correctly.
HOWEVER, the current python's httplib and requests module used by python-swiftclient doesn't
support expects: 100-continue header [2] and the thread proposed a fix [3] is not super active.
And we know the reason we depends on the library is to fix a security issue that existed
in older python-swiftclient [4] so that we should touch around it super carefully.
In the reality, as the hot fix, this patch try to mitigate the unfortunate situation
described above WITHOUT 100-continue fix, just users can force to re-auth when any errors
occurred during the retries that can be accepted in the upstream.
1: http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close
2: https://github.com/requests/requests/issues/713
3: https://bugs.python.org/issue1346874
4: https://review.openstack.org/#/c/69187/
Change-Id: I3470b56e3f9cf9cdb8c2fc2a94b2c551927a3440
2018-03-12 17:54:17 +09:00
|
|
|
do_test(500, 6)
|
|
|
|
do_test(503, 6)
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_resp_read_on_server_error(self):
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf', retries=0)
|
|
|
|
|
|
|
|
def get_auth(*args, **kwargs):
|
|
|
|
return 'http://www.new.com', 'new'
|
|
|
|
conn.get_auth = get_auth
|
|
|
|
self.url, self.token = conn.get_auth()
|
|
|
|
|
|
|
|
method_signatures = (
|
|
|
|
(conn.head_account, []),
|
|
|
|
(conn.get_account, []),
|
|
|
|
(conn.head_container, ('asdf',)),
|
|
|
|
(conn.get_container, ('asdf',)),
|
|
|
|
(conn.put_container, ('asdf',)),
|
|
|
|
(conn.delete_container, ('asdf',)),
|
|
|
|
(conn.head_object, ('asdf', 'asdf')),
|
|
|
|
(conn.get_object, ('asdf', 'asdf')),
|
|
|
|
(conn.put_object, ('asdf', 'asdf', 'asdf')),
|
|
|
|
(conn.post_object, ('asdf', 'asdf', {})),
|
|
|
|
(conn.delete_object, ('asdf', 'asdf')),
|
|
|
|
)
|
|
|
|
|
|
|
|
for method, args in method_signatures:
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(500)
|
2012-05-08 15:51:15 +01:00
|
|
|
self.assertRaises(c.ClientException, method, *args)
|
2014-10-24 01:02:53 -07:00
|
|
|
requests = list(self.iter_request_log())
|
|
|
|
self.assertEqual(len(requests), 1)
|
|
|
|
for req in requests:
|
2012-05-08 15:51:15 +01:00
|
|
|
msg = '%s did not read resp on server error' % method.__name__
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertTrue(req['resp'].has_been_read, msg)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def test_reauth(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(401, 200)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def get_auth(*args, **kwargs):
|
2014-10-24 01:02:53 -07:00
|
|
|
# this mock, and by extension this test are not
|
2015-12-29 16:54:05 -08:00
|
|
|
# representative of the unit under test. The real get_auth
|
2014-10-24 01:02:53 -07:00
|
|
|
# method will always return the os_option dict's
|
|
|
|
# object_storage_url which will be overridden by the
|
2015-05-13 09:48:41 +00:00
|
|
|
# preauthurl parameter to Connection if it is provided.
|
2012-05-08 15:51:15 +01:00
|
|
|
return 'http://www.new.com', 'new'
|
|
|
|
|
|
|
|
def swap_sleep(*args):
|
|
|
|
self.swap_sleep_called = True
|
|
|
|
c.get_auth = get_auth
|
|
|
|
c.sleep = swap_sleep
|
|
|
|
self.swap_sleep_called = False
|
|
|
|
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
preauthurl='http://www.old.com',
|
|
|
|
preauthtoken='old',
|
2013-03-22 14:59:57 +00:00
|
|
|
)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(conn.attempts, 0)
|
|
|
|
self.assertEqual(conn.url, 'http://www.old.com')
|
|
|
|
self.assertEqual(conn.token, 'old')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2013-04-28 19:16:38 -07:00
|
|
|
conn.head_account()
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
self.assertTrue(self.swap_sleep_called)
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(conn.attempts, 2)
|
|
|
|
self.assertEqual(conn.url, 'http://www.new.com')
|
|
|
|
self.assertEqual(conn.token, 'new')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2014-10-24 01:02:53 -07:00
|
|
|
def test_reauth_preauth(self):
|
|
|
|
conn = c.Connection(
|
|
|
|
'http://auth.example.com', 'user', 'password',
|
|
|
|
preauthurl='http://storage.example.com/v1/AUTH_test',
|
|
|
|
preauthtoken='expired')
|
|
|
|
auth_v1_response = StubResponse(200, headers={
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'x-storage-url': 'http://storage.example.com/v1/AUTH_user',
|
|
|
|
})
|
|
|
|
fake_conn = self.fake_http_connection(401, auth_v1_response, 200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'expired'}),
|
|
|
|
('GET', 'http://auth.example.com', '', {
|
|
|
|
'x-auth-user': 'user',
|
|
|
|
'x-auth-key': 'password'}),
|
|
|
|
('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_reauth_os_preauth(self):
|
|
|
|
os_preauth_options = {
|
|
|
|
'tenant_name': 'demo',
|
|
|
|
'object_storage_url': 'http://storage.example.com/v1/AUTH_test',
|
|
|
|
'auth_token': 'expired',
|
|
|
|
}
|
|
|
|
conn = c.Connection('http://auth.example.com', 'user', 'password',
|
|
|
|
os_options=os_preauth_options, auth_version=2)
|
|
|
|
fake_keystone = fake_get_auth_keystone(os_preauth_options)
|
|
|
|
fake_conn = self.fake_http_connection(401, 200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
get_auth_keystone=fake_keystone,
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'expired'}),
|
|
|
|
('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
|
2016-03-29 19:10:42 +00:00
|
|
|
def test_session_no_invalidate(self):
|
|
|
|
mock_session = mock.MagicMock()
|
|
|
|
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
|
|
|
|
mock_session.get_token.return_value = 'expired'
|
|
|
|
mock_session.invalidate.return_value = False
|
|
|
|
conn = c.Connection(session=mock_session)
|
|
|
|
fake_conn = self.fake_http_connection(401)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
self.assertRaises(c.ClientException, conn.head_account)
|
|
|
|
self.assertEqual(mock_session.get_token.mock_calls, [mock.call()])
|
|
|
|
self.assertEqual(mock_session.invalidate.mock_calls, [mock.call()])
|
|
|
|
|
|
|
|
def test_session_can_invalidate(self):
|
|
|
|
mock_session = mock.MagicMock()
|
|
|
|
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
|
|
|
|
mock_session.get_token.side_effect = ['expired', 'token']
|
|
|
|
mock_session.invalidate.return_value = True
|
|
|
|
conn = c.Connection(session=mock_session)
|
|
|
|
fake_conn = self.fake_http_connection(401, 200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/acct', '', {'x-auth-token': 'expired'}),
|
|
|
|
('HEAD', '/v1/acct', '', {'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
self.assertEqual(mock_session.get_token.mock_calls, [
|
|
|
|
mock.call(), mock.call()])
|
|
|
|
self.assertEqual(mock_session.invalidate.mock_calls, [mock.call()])
|
|
|
|
|
2014-10-24 01:02:53 -07:00
|
|
|
def test_preauth_token_with_no_storage_url_requires_auth(self):
|
|
|
|
conn = c.Connection(
|
|
|
|
'http://auth.example.com', 'user', 'password',
|
|
|
|
preauthtoken='expired')
|
|
|
|
auth_v1_response = StubResponse(200, headers={
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'x-storage-url': 'http://storage.example.com/v1/AUTH_user',
|
|
|
|
})
|
|
|
|
fake_conn = self.fake_http_connection(auth_v1_response, 200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://auth.example.com', '', {
|
|
|
|
'x-auth-user': 'user',
|
|
|
|
'x-auth-key': 'password'}),
|
|
|
|
('HEAD', '/v1/AUTH_user', '', {'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_os_preauth_token_with_no_storage_url_requires_auth(self):
|
|
|
|
os_preauth_options = {
|
|
|
|
'tenant_name': 'demo',
|
|
|
|
'auth_token': 'expired',
|
|
|
|
}
|
|
|
|
conn = c.Connection('http://auth.example.com', 'user', 'password',
|
|
|
|
os_options=os_preauth_options, auth_version=2)
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_user'
|
|
|
|
fake_keystone = fake_get_auth_keystone(storage_url=storage_url)
|
|
|
|
fake_conn = self.fake_http_connection(200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
get_auth_keystone=fake_keystone,
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/AUTH_user', '', {'x-auth-token': 'token'}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_preauth_url_trumps_auth_url(self):
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_pre_url'
|
|
|
|
conn = c.Connection(
|
|
|
|
'http://auth.example.com', 'user', 'password',
|
|
|
|
preauthurl=storage_url)
|
|
|
|
auth_v1_response = StubResponse(200, headers={
|
|
|
|
'x-auth-token': 'post_token',
|
|
|
|
'x-storage-url': 'http://storage.example.com/v1/AUTH_post_url',
|
|
|
|
})
|
|
|
|
fake_conn = self.fake_http_connection(auth_v1_response, 200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', 'http://auth.example.com', '', {
|
|
|
|
'x-auth-user': 'user',
|
|
|
|
'x-auth-key': 'password'}),
|
|
|
|
('HEAD', '/v1/AUTH_pre_url', '', {'x-auth-token': 'post_token'}),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_os_preauth_url_trumps_auth_url(self):
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_pre_url'
|
|
|
|
os_preauth_options = {
|
|
|
|
'tenant_name': 'demo',
|
|
|
|
'object_storage_url': storage_url,
|
|
|
|
}
|
|
|
|
conn = c.Connection('http://auth.example.com', 'user', 'password',
|
|
|
|
os_options=os_preauth_options, auth_version=2)
|
|
|
|
fake_keystone = fake_get_auth_keystone(
|
|
|
|
storage_url='http://storage.example.com/v1/AUTH_post_url',
|
|
|
|
token='post_token')
|
|
|
|
fake_conn = self.fake_http_connection(200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
get_auth_keystone=fake_keystone,
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/AUTH_pre_url', '', {'x-auth-token': 'post_token'}),
|
|
|
|
])
|
|
|
|
|
2015-08-24 12:34:45 +01:00
|
|
|
def test_preauth_url_trumps_os_preauth_url(self):
|
|
|
|
storage_url = 'http://storage.example.com/v1/AUTH_pre_url'
|
|
|
|
os_storage_url = 'http://storage.example.com/v1/AUTH_os_pre_url'
|
|
|
|
os_preauth_options = {
|
|
|
|
'tenant_name': 'demo',
|
|
|
|
'object_storage_url': os_storage_url,
|
|
|
|
}
|
|
|
|
orig_os_preauth_options = dict(os_preauth_options)
|
|
|
|
conn = c.Connection('http://auth.example.com', 'user', 'password',
|
|
|
|
os_options=os_preauth_options, auth_version=2,
|
|
|
|
preauthurl=storage_url, tenant_name='not_demo')
|
|
|
|
fake_keystone = fake_get_auth_keystone(
|
|
|
|
storage_url='http://storage.example.com/v1/AUTH_post_url',
|
|
|
|
token='post_token')
|
|
|
|
fake_conn = self.fake_http_connection(200)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
get_auth_keystone=fake_keystone,
|
|
|
|
http_connection=fake_conn,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/AUTH_pre_url', '', {'x-auth-token': 'post_token'}),
|
|
|
|
])
|
|
|
|
|
|
|
|
# check that Connection has not modified our os_options
|
|
|
|
self.assertEqual(orig_os_preauth_options, os_preauth_options)
|
|
|
|
|
2015-07-24 10:57:29 -07:00
|
|
|
def test_get_auth_sets_url_and_token(self):
|
|
|
|
with mock.patch('swiftclient.client.get_auth') as mock_get_auth:
|
|
|
|
mock_get_auth.return_value = (
|
|
|
|
"https://storage.url/v1/AUTH_storage_acct", "AUTH_token"
|
|
|
|
)
|
2015-07-17 16:16:07 +09:00
|
|
|
conn = c.Connection("https://auth.url/auth/v2.0",
|
|
|
|
"user", "passkey", tenant_name="tenant")
|
2015-07-24 10:57:29 -07:00
|
|
|
conn.get_auth()
|
|
|
|
self.assertEqual("https://storage.url/v1/AUTH_storage_acct", conn.url)
|
|
|
|
self.assertEqual("AUTH_token", conn.token)
|
|
|
|
|
2015-04-26 01:37:15 -06:00
|
|
|
def test_timeout_passed_down(self):
|
|
|
|
# We want to avoid mocking http_connection(), and most especially
|
|
|
|
# avoid passing it down in argument. However, we cannot simply
|
|
|
|
# instantiate C=Connection(), then shim C.http_conn. Doing so would
|
|
|
|
# avoid some of the code under test (where _retry() invokes
|
|
|
|
# http_connection()), and would miss get_auth() completely.
|
|
|
|
# So, with regret, we do mock http_connection(), but with a very
|
|
|
|
# light shim that swaps out _request() as originally intended.
|
|
|
|
|
|
|
|
orig_http_connection = c.http_connection
|
|
|
|
|
|
|
|
timeouts = []
|
|
|
|
|
|
|
|
def my_request_handler(*a, **kw):
|
|
|
|
if 'timeout' in kw:
|
|
|
|
timeouts.append(kw['timeout'])
|
|
|
|
else:
|
|
|
|
timeouts.append(None)
|
|
|
|
return MockHttpResponse(
|
|
|
|
status=200,
|
|
|
|
headers={
|
|
|
|
'x-auth-token': 'a_token',
|
|
|
|
'x-storage-url': 'http://files.example.com/v1/AUTH_user'})
|
|
|
|
|
|
|
|
def shim_connection(*a, **kw):
|
|
|
|
url, conn = orig_http_connection(*a, **kw)
|
|
|
|
conn._request = my_request_handler
|
|
|
|
return url, conn
|
|
|
|
|
2015-04-28 16:56:44 +01:00
|
|
|
# v1 auth
|
2015-04-26 01:37:15 -06:00
|
|
|
conn = c.Connection(
|
|
|
|
'http://auth.example.com', 'user', 'password', timeout=33.0)
|
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=shim_connection,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
|
|
|
|
# 1 call is through get_auth, 1 call is HEAD for account
|
|
|
|
self.assertEqual(timeouts, [33.0, 33.0])
|
|
|
|
|
2015-04-28 16:56:44 +01:00
|
|
|
# v2 auth
|
|
|
|
timeouts = []
|
2015-04-12 22:33:57 -05:00
|
|
|
os_options = {'tenant_name': 'tenant', 'auth_token': 'meta-token'}
|
2015-04-28 16:56:44 +01:00
|
|
|
conn = c.Connection(
|
|
|
|
'http://auth.example.com', 'user', 'password', timeout=33.0,
|
2015-04-12 22:33:57 -05:00
|
|
|
os_options=os_options, auth_version=2.0)
|
2015-04-28 16:56:44 +01:00
|
|
|
fake_ks = FakeKeystone(endpoint='http://some_url', token='secret')
|
2018-09-07 16:50:10 -07:00
|
|
|
with mock.patch('swiftclient.client.ksclient_v2', fake_ks):
|
2015-04-28 16:56:44 +01:00
|
|
|
with mock.patch.multiple('swiftclient.client',
|
|
|
|
http_connection=shim_connection,
|
|
|
|
sleep=mock.DEFAULT):
|
|
|
|
conn.head_account()
|
|
|
|
|
|
|
|
# check timeout is passed to keystone client
|
|
|
|
self.assertEqual(1, len(fake_ks.calls))
|
|
|
|
self.assertEqual(33.0, fake_ks.calls[0].get('timeout'))
|
|
|
|
# check timeout passed to HEAD for account
|
|
|
|
self.assertEqual(timeouts, [33.0])
|
|
|
|
|
2015-04-12 22:33:57 -05:00
|
|
|
# check token passed to keystone client
|
|
|
|
self.assertIn('token', fake_ks.calls[0])
|
|
|
|
self.assertEqual('meta-token', fake_ks.calls[0].get('token'))
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def test_reset_stream(self):
|
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class LocalContents:
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def __init__(self, tell_value=0):
|
2022-02-17 11:37:13 +00:00
|
|
|
self.data = io.BytesIO(string.ascii_letters.encode() * 10)
|
2015-09-23 10:42:43 -07:00
|
|
|
self.data.seek(tell_value)
|
|
|
|
self.reads = []
|
2012-05-08 15:51:15 +01:00
|
|
|
self.seeks = []
|
2015-09-23 10:42:43 -07:00
|
|
|
self.tells = []
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def tell(self):
|
2015-09-23 10:42:43 -07:00
|
|
|
self.tells.append(self.data.tell())
|
|
|
|
return self.tells[-1]
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-09-23 10:42:43 -07:00
|
|
|
def seek(self, position, mode=0):
|
|
|
|
self.seeks.append((position, mode))
|
|
|
|
self.data.seek(position, mode)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def read(self, size=-1):
|
2015-09-23 10:42:43 -07:00
|
|
|
read_data = self.data.read(size)
|
|
|
|
self.reads.append((size, read_data))
|
|
|
|
return read_data
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2022-03-21 18:19:50 +00:00
|
|
|
class LocalConnection:
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def __init__(self, parsed_url=None):
|
|
|
|
self.reason = ""
|
|
|
|
if parsed_url:
|
|
|
|
self.host = parsed_url.netloc
|
|
|
|
self.port = parsed_url.netloc
|
|
|
|
|
|
|
|
def putrequest(self, *args, **kwargs):
|
2015-09-23 10:42:43 -07:00
|
|
|
self.send('PUT', *args, **kwargs)
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
def putheader(self, *args, **kwargs):
|
|
|
|
return
|
|
|
|
|
|
|
|
def endheaders(self, *args, **kwargs):
|
|
|
|
return
|
|
|
|
|
|
|
|
def send(self, *args, **kwargs):
|
2015-09-23 10:42:43 -07:00
|
|
|
data = kwargs.get('data')
|
|
|
|
if data is not None:
|
|
|
|
if hasattr(data, 'read'):
|
|
|
|
data.read()
|
|
|
|
else:
|
|
|
|
for datum in data:
|
|
|
|
pass
|
2012-05-08 15:51:15 +01:00
|
|
|
raise socket.error('oops')
|
|
|
|
|
|
|
|
def request(self, *args, **kwargs):
|
|
|
|
return
|
|
|
|
|
|
|
|
def getresponse(self, *args, **kwargs):
|
|
|
|
self.status = 200
|
|
|
|
return self
|
|
|
|
|
|
|
|
def getheader(self, *args, **kwargs):
|
|
|
|
return 'header'
|
|
|
|
|
2013-10-15 09:46:46 -07:00
|
|
|
def getheaders(self):
|
2017-03-07 16:52:51 -08:00
|
|
|
return [('key1', 'value1'), ('key2', 'value2')]
|
2013-10-15 09:46:46 -07:00
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
def read(self, *args, **kwargs):
|
|
|
|
return ''
|
|
|
|
|
2018-06-22 16:49:03 -07:00
|
|
|
def close(self):
|
|
|
|
pass
|
|
|
|
|
2014-01-24 17:40:16 +01:00
|
|
|
def local_http_connection(url, proxy=None, cacert=None,
|
2016-04-10 23:18:17 +02:00
|
|
|
insecure=False, cert=None, cert_key=None,
|
|
|
|
ssl_compression=True, timeout=None):
|
2012-05-08 15:51:15 +01:00
|
|
|
parsed = urlparse(url)
|
|
|
|
return parsed, LocalConnection()
|
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
with mock.patch.object(c, 'http_connection', local_http_connection):
|
2012-05-08 15:51:15 +01:00
|
|
|
conn = c.Connection('http://www.example.com', 'asdf', 'asdf',
|
|
|
|
retries=1, starting_backoff=.0001)
|
|
|
|
|
|
|
|
contents = LocalContents()
|
|
|
|
exc = None
|
|
|
|
try:
|
|
|
|
conn.put_object('c', 'o', contents)
|
2013-04-25 23:19:52 +02:00
|
|
|
except socket.error as err:
|
2012-05-08 15:51:15 +01:00
|
|
|
exc = err
|
2015-09-23 10:42:43 -07:00
|
|
|
self.assertEqual(contents.tells, [0])
|
|
|
|
self.assertEqual(contents.seeks, [(0, 0)])
|
|
|
|
# four reads: two in the initial pass, two in the retry
|
|
|
|
self.assertEqual(4, len(contents.reads))
|
|
|
|
self.assertEqual((65536, b''), contents.reads[1])
|
|
|
|
self.assertEqual((65536, b''), contents.reads[3])
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(str(exc), 'oops')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
|
|
|
contents = LocalContents(tell_value=123)
|
|
|
|
exc = None
|
|
|
|
try:
|
|
|
|
conn.put_object('c', 'o', contents)
|
2013-04-25 23:19:52 +02:00
|
|
|
except socket.error as err:
|
2012-05-08 15:51:15 +01:00
|
|
|
exc = err
|
2015-09-23 10:42:43 -07:00
|
|
|
self.assertEqual(contents.tells, [123])
|
|
|
|
self.assertEqual(contents.seeks, [(123, 0)])
|
|
|
|
# four reads: two in the initial pass, two in the retry
|
|
|
|
self.assertEqual(4, len(contents.reads))
|
|
|
|
self.assertEqual((65536, b''), contents.reads[1])
|
|
|
|
self.assertEqual((65536, b''), contents.reads[3])
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(str(exc), 'oops')
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-09-23 10:42:43 -07:00
|
|
|
contents = LocalContents(tell_value=123)
|
|
|
|
wrapped_contents = swiftclient.utils.LengthWrapper(
|
|
|
|
contents, 6, md5=True)
|
|
|
|
exc = None
|
|
|
|
try:
|
|
|
|
conn.put_object('c', 'o', wrapped_contents)
|
|
|
|
except socket.error as err:
|
|
|
|
exc = err
|
|
|
|
self.assertEqual(contents.tells, [123])
|
|
|
|
self.assertEqual(contents.seeks, [(123, 0)])
|
|
|
|
self.assertEqual(contents.reads, [(6, b'tuvwxy')] * 2)
|
|
|
|
self.assertEqual(str(exc), 'oops')
|
|
|
|
self.assertEqual(md5(b'tuvwxy').hexdigest(),
|
|
|
|
wrapped_contents.get_md5sum())
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
contents = LocalContents()
|
|
|
|
contents.tell = None
|
|
|
|
exc = None
|
|
|
|
try:
|
|
|
|
conn.put_object('c', 'o', contents)
|
2013-04-25 23:19:52 +02:00
|
|
|
except c.ClientException as err:
|
2012-05-08 15:51:15 +01:00
|
|
|
exc = err
|
2013-09-30 22:23:28 +02:00
|
|
|
self.assertEqual(contents.seeks, [])
|
|
|
|
self.assertEqual(str(exc), "put_object('c', 'o', ...) failure "
|
|
|
|
"and no ability to reset contents for reupload.")
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2015-09-22 11:09:44 +01:00
|
|
|
def test_get_container(self):
|
|
|
|
headers = {'X-Favourite-Pet': 'Aardvark'}
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200, body=b'{}')):
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
lambda *a, **k: ('http://url:8080/v1/a', 'token')):
|
|
|
|
conn = c.Connection()
|
|
|
|
conn.get_container('c1', prefix='p', limit=5,
|
|
|
|
headers=headers)
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
self.assertRequests([
|
|
|
|
('GET', '/v1/a/c1?format=json&limit=5&prefix=p', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'X-Favourite-Pet': 'Aardvark',
|
2015-05-21 22:44:36 -07:00
|
|
|
'accept-encoding': 'gzip',
|
2015-09-22 11:09:44 +01:00
|
|
|
}),
|
|
|
|
])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_head_container(self):
|
|
|
|
headers = {'X-Favourite-Pet': 'Aardvark'}
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200, body=b'{}')):
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
lambda *a, **k: ('http://url:8080/v1/a', 'token')):
|
|
|
|
conn = c.Connection()
|
|
|
|
conn.head_container('c1', headers=headers)
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
self.assertRequests([
|
|
|
|
('HEAD', '/v1/a/c1', '', {
|
|
|
|
'x-auth-token': 'token',
|
|
|
|
'X-Favourite-Pet': 'Aardvark',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_head_object(self):
|
|
|
|
headers = {'X-Favourite-Pet': 'Aardvark'}
|
2018-03-05 17:33:22 -08:00
|
|
|
query_string = 'foo=bar'
|
2015-09-22 11:09:44 +01:00
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
lambda *a, **k: ('http://url:8080/v1/a', 'token')):
|
|
|
|
conn = c.Connection()
|
|
|
|
conn.head_object('c1', 'o1',
|
2018-03-05 17:33:22 -08:00
|
|
|
headers=headers, query_string=query_string)
|
2015-09-22 11:09:44 +01:00
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
self.assertRequests([
|
2018-03-05 17:33:22 -08:00
|
|
|
('HEAD', '/v1/a/c1/o1?foo=bar', '', {
|
2015-09-22 11:09:44 +01:00
|
|
|
'x-auth-token': 'token',
|
|
|
|
'X-Favourite-Pet': 'Aardvark',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
2012-05-08 15:51:15 +01:00
|
|
|
|
2014-10-15 18:56:58 +01:00
|
|
|
class TestResponseDict(MockHttpTest):
|
|
|
|
"""
|
|
|
|
Verify handling of optional response_dict argument.
|
|
|
|
"""
|
2015-06-11 14:33:39 -07:00
|
|
|
calls = [('post_account', {}),
|
|
|
|
('post_container', 'c', {}),
|
2014-10-15 18:56:58 +01:00
|
|
|
('put_container', 'c'),
|
|
|
|
('delete_container', 'c'),
|
|
|
|
('post_object', 'c', 'o', {}),
|
|
|
|
('put_object', 'c', 'o', 'body'),
|
2016-02-15 12:14:17 +01:00
|
|
|
('copy_object', 'c', 'o'),
|
2014-10-15 18:56:58 +01:00
|
|
|
('delete_object', 'c', 'o')]
|
|
|
|
|
|
|
|
def fake_get_auth(*args, **kwargs):
|
|
|
|
return 'http://url', 'token'
|
|
|
|
|
|
|
|
def test_response_dict_with_auth_error(self):
|
|
|
|
def bad_get_auth(*args, **kwargs):
|
|
|
|
raise c.ClientException('test')
|
|
|
|
|
|
|
|
for call in self.calls:
|
|
|
|
resp_dict = {'test': 'should be untouched'}
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
bad_get_auth):
|
|
|
|
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
|
|
|
|
self.assertRaises(c.ClientException, getattr(conn, call[0]),
|
|
|
|
*call[1:], response_dict=resp_dict)
|
|
|
|
|
|
|
|
self.assertEqual({'test': 'should be untouched'}, resp_dict)
|
|
|
|
|
|
|
|
def test_response_dict_with_request_error(self):
|
|
|
|
for call in self.calls:
|
|
|
|
resp_dict = {'test': 'should be untouched'}
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
self.fake_get_auth):
|
|
|
|
exc = c.ClientException('test')
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200, exc=exc)):
|
|
|
|
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
|
|
|
|
self.assertRaises(c.ClientException,
|
|
|
|
getattr(conn, call[0]),
|
|
|
|
*call[1:],
|
|
|
|
response_dict=resp_dict)
|
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('should be untouched', resp_dict.get('test'))
|
|
|
|
self.assertEqual([{}], resp_dict.get('response_dicts'))
|
2014-10-15 18:56:58 +01:00
|
|
|
|
|
|
|
def test_response_dict(self):
|
|
|
|
# test response_dict is populated and
|
|
|
|
# new list of response_dicts is created
|
|
|
|
for call in self.calls:
|
|
|
|
resp_dict = {'test': 'should be untouched'}
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
self.fake_get_auth):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
|
|
|
|
getattr(conn, call[0])(*call[1:], response_dict=resp_dict)
|
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('should be untouched',
|
|
|
|
resp_dict.pop('test', None))
|
|
|
|
self.assertEqual('Fake', resp_dict.get('reason'))
|
|
|
|
self.assertEqual(200, resp_dict.get('status'))
|
|
|
|
self.assertIn('headers', resp_dict)
|
|
|
|
self.assertEqual('yes', resp_dict['headers'].get('x-works'))
|
|
|
|
children = resp_dict.pop('response_dicts', [])
|
2014-10-15 18:56:58 +01:00
|
|
|
self.assertEqual(1, len(children))
|
|
|
|
self.assertEqual(resp_dict, children[0])
|
|
|
|
|
|
|
|
def test_response_dict_with_existing(self):
|
|
|
|
# check response_dict is populated and new dict is appended
|
|
|
|
# to existing response_dicts list
|
|
|
|
for call in self.calls:
|
|
|
|
resp_dict = {'test': 'should be untouched',
|
|
|
|
'response_dicts': [{'existing': 'response dict'}]}
|
|
|
|
with mock.patch('swiftclient.client.get_auth',
|
|
|
|
self.fake_get_auth):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
|
|
|
|
getattr(conn, call[0])(*call[1:], response_dict=resp_dict)
|
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('should be untouched',
|
|
|
|
resp_dict.pop('test', None))
|
|
|
|
self.assertEqual('Fake', resp_dict.get('reason'))
|
|
|
|
self.assertEqual(200, resp_dict.get('status'))
|
|
|
|
self.assertIn('headers', resp_dict)
|
|
|
|
self.assertEqual('yes', resp_dict['headers'].get('x-works'))
|
|
|
|
children = resp_dict.pop('response_dicts', [])
|
2014-10-15 18:56:58 +01:00
|
|
|
self.assertEqual(2, len(children))
|
|
|
|
self.assertEqual({'existing': 'response dict'}, children[0])
|
|
|
|
self.assertEqual(resp_dict, children[1])
|
|
|
|
|
|
|
|
|
2013-10-15 09:46:46 -07:00
|
|
|
class TestLogging(MockHttpTest):
|
|
|
|
"""
|
|
|
|
Make sure all the lines in http_log are covered.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
super(TestLogging, self).setUp()
|
|
|
|
self.swiftclient_logger = logging.getLogger("swiftclient")
|
|
|
|
self.log_level = self.swiftclient_logger.getEffectiveLevel()
|
|
|
|
self.swiftclient_logger.setLevel(logging.INFO)
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.swiftclient_logger.setLevel(self.log_level)
|
|
|
|
super(TestLogging, self).tearDown()
|
|
|
|
|
|
|
|
def test_put_ok(self):
|
|
|
|
c.http_connection = self.fake_http_connection(200)
|
|
|
|
args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
|
|
|
|
value = c.put_object(*args)
|
2022-02-17 11:37:13 +00:00
|
|
|
self.assertIsInstance(value, str)
|
2013-10-15 09:46:46 -07:00
|
|
|
|
|
|
|
def test_head_error(self):
|
|
|
|
c.http_connection = self.fake_http_connection(500)
|
|
|
|
self.assertRaises(c.ClientException, c.head_object,
|
|
|
|
'http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
|
|
|
|
def test_get_error(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection(404)
|
2015-12-04 11:28:05 -08:00
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.get_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
self.assertEqual(exc_context.exception.http_status, 404)
|
2013-10-15 09:46:46 -07:00
|
|
|
|
2017-03-07 16:52:51 -08:00
|
|
|
def test_content_encoding_gzip_body_is_logged_decoded(self):
|
2022-02-17 11:37:13 +00:00
|
|
|
buf = io.BytesIO()
|
2017-03-07 16:52:51 -08:00
|
|
|
gz = gzip.GzipFile(fileobj=buf, mode='w')
|
2022-03-21 18:17:41 +00:00
|
|
|
data = {"test": "\u2603"}
|
2017-03-07 16:52:51 -08:00
|
|
|
decoded_body = json.dumps(data).encode('utf-8')
|
|
|
|
gz.write(decoded_body)
|
|
|
|
gz.close()
|
|
|
|
# stub a gzip encoded body
|
|
|
|
body = buf.getvalue()
|
|
|
|
headers = {'content-encoding': 'gzip'}
|
|
|
|
# ... and make a content-encoding gzip error response
|
|
|
|
stub_response = StubResponse(500, body, headers)
|
|
|
|
with mock.patch('swiftclient.client.logger.info') as mock_log:
|
|
|
|
# ... if the client gets such a response
|
|
|
|
c.http_connection = self.fake_http_connection(stub_response)
|
|
|
|
with self.assertRaises(c.ClientException) as exc_context:
|
|
|
|
c.get_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
|
|
|
|
self.assertEqual(exc_context.exception.http_status, 500)
|
|
|
|
# it will log the decoded body
|
|
|
|
self.assertEqual([
|
2022-03-21 18:17:41 +00:00
|
|
|
mock.call('REQ: %s', 'curl -i http://www.test.com/asdf/asdf '
|
2017-03-07 16:52:51 -08:00
|
|
|
'-X GET -H "X-Auth-Token: ..."'),
|
|
|
|
mock.call('RESP STATUS: %s %s', 500, 'Fake'),
|
|
|
|
mock.call('RESP HEADERS: %s', {'content-encoding': 'gzip'}),
|
|
|
|
mock.call('RESP BODY: %s', decoded_body)
|
|
|
|
], mock_log.mock_calls)
|
|
|
|
|
2016-02-19 13:18:15 +00:00
|
|
|
def test_redact_token(self):
|
|
|
|
with mock.patch('swiftclient.client.logger.debug') as mock_log:
|
|
|
|
token_value = 'tkee96b40a8ca44fc5ad72ec5a7c90d9b'
|
2016-02-25 17:13:35 +00:00
|
|
|
token_encoded = token_value.encode('utf8')
|
2022-03-21 18:17:41 +00:00
|
|
|
unicode_token_value = ('\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c')
|
2016-02-25 17:13:35 +00:00
|
|
|
unicode_token_encoded = unicode_token_value.encode('utf8')
|
2016-02-19 13:18:15 +00:00
|
|
|
set_cookie_value = 'X-Auth-Token=%s' % token_value
|
2016-02-25 17:13:35 +00:00
|
|
|
set_cookie_encoded = set_cookie_value.encode('utf8')
|
2016-02-19 13:18:15 +00:00
|
|
|
c.http_log(
|
|
|
|
['GET'],
|
|
|
|
{'headers': {
|
2016-02-25 17:13:35 +00:00
|
|
|
'X-Auth-Token': token_encoded,
|
|
|
|
'X-Storage-Token': unicode_token_encoded
|
2016-02-19 13:18:15 +00:00
|
|
|
}},
|
|
|
|
MockHttpResponse(
|
|
|
|
status=200,
|
|
|
|
headers={
|
2016-02-25 17:13:35 +00:00
|
|
|
'X-Auth-Token': token_encoded,
|
|
|
|
'X-Storage-Token': unicode_token_encoded,
|
2016-02-19 13:18:15 +00:00
|
|
|
'Etag': b'mock_etag',
|
2016-02-25 17:13:35 +00:00
|
|
|
'Set-Cookie': set_cookie_encoded
|
2016-02-19 13:18:15 +00:00
|
|
|
}
|
|
|
|
),
|
|
|
|
''
|
|
|
|
)
|
|
|
|
out = []
|
|
|
|
for _, args, kwargs in mock_log.mock_calls:
|
|
|
|
for arg in args:
|
2022-03-21 18:17:41 +00:00
|
|
|
out.append('%s' % arg)
|
|
|
|
output = ''.join(out)
|
2016-02-19 13:18:15 +00:00
|
|
|
self.assertIn('X-Auth-Token', output)
|
|
|
|
self.assertIn(token_value[:16] + '...', output)
|
|
|
|
self.assertIn('X-Storage-Token', output)
|
|
|
|
self.assertIn(unicode_token_value[:8] + '...', output)
|
|
|
|
self.assertIn('Set-Cookie', output)
|
|
|
|
self.assertIn(set_cookie_value[:16] + '...', output)
|
|
|
|
self.assertNotIn(token_value, output)
|
|
|
|
self.assertNotIn(unicode_token_value, output)
|
|
|
|
self.assertNotIn(set_cookie_value, output)
|
|
|
|
|
|
|
|
def test_show_token(self):
|
|
|
|
with mock.patch('swiftclient.client.logger.debug') as mock_log:
|
|
|
|
token_value = 'tkee96b40a8ca44fc5ad72ec5a7c90d9b'
|
2016-02-25 17:13:35 +00:00
|
|
|
token_encoded = token_value.encode('utf8')
|
2022-03-21 18:17:41 +00:00
|
|
|
unicode_token_value = ('\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
|
|
|
|
'\u5929\u7a7a\u4e2d\u7684\u4e4c')
|
2016-02-24 16:56:55 -08:00
|
|
|
c.logger_settings['redact_sensitive_headers'] = False
|
2016-02-25 17:13:35 +00:00
|
|
|
unicode_token_encoded = unicode_token_value.encode('utf8')
|
2016-02-19 13:18:15 +00:00
|
|
|
c.http_log(
|
|
|
|
['GET'],
|
|
|
|
{'headers': {
|
2016-02-25 17:13:35 +00:00
|
|
|
'X-Auth-Token': token_encoded,
|
|
|
|
'X-Storage-Token': unicode_token_encoded
|
2016-02-19 13:18:15 +00:00
|
|
|
}},
|
|
|
|
MockHttpResponse(
|
|
|
|
status=200,
|
|
|
|
headers=[
|
2016-02-25 17:13:35 +00:00
|
|
|
('X-Auth-Token', token_encoded),
|
|
|
|
('X-Storage-Token', unicode_token_encoded),
|
2016-02-19 13:18:15 +00:00
|
|
|
('Etag', b'mock_etag')
|
|
|
|
]
|
|
|
|
),
|
|
|
|
''
|
|
|
|
)
|
|
|
|
out = []
|
|
|
|
for _, args, kwargs in mock_log.mock_calls:
|
|
|
|
for arg in args:
|
2022-03-21 18:17:41 +00:00
|
|
|
out.append('%s' % arg)
|
|
|
|
output = ''.join(out)
|
2016-02-19 13:18:15 +00:00
|
|
|
self.assertIn('X-Auth-Token', output)
|
|
|
|
self.assertIn(token_value, output)
|
|
|
|
self.assertIn('X-Storage-Token', output)
|
|
|
|
self.assertIn(unicode_token_value, output)
|
|
|
|
|
2018-04-17 14:36:57 -07:00
|
|
|
@mock.patch('swiftclient.client.logger.debug')
|
|
|
|
def test_unicode_path(self, mock_log):
|
2022-03-21 18:17:41 +00:00
|
|
|
path = 'http://swift/v1/AUTH_account-\u062a'.encode('utf-8')
|
2018-04-17 14:36:57 -07:00
|
|
|
c.http_log(['GET', path], {},
|
|
|
|
MockHttpResponse(status=200, headers=[]), '')
|
|
|
|
request_log_line = mock_log.mock_calls[0]
|
|
|
|
self.assertEqual('REQ: %s', request_log_line[1][0])
|
2022-03-21 18:17:41 +00:00
|
|
|
self.assertEqual('curl -i -X GET %s' % path.decode('utf-8'),
|
2018-04-17 14:36:57 -07:00
|
|
|
request_log_line[1][1])
|
|
|
|
|
2013-10-15 09:46:46 -07:00
|
|
|
|
2013-10-17 11:12:06 +08:00
|
|
|
class TestCloseConnection(MockHttpTest):
|
|
|
|
|
|
|
|
def test_close_none(self):
|
2014-10-24 01:02:53 -07:00
|
|
|
c.http_connection = self.fake_http_connection()
|
2013-10-17 11:12:06 +08:00
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(conn.http_conn)
|
2013-10-17 11:12:06 +08:00
|
|
|
conn.close()
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(conn.http_conn)
|
2018-06-22 16:49:03 -07:00
|
|
|
# Can re-close
|
|
|
|
conn.close()
|
|
|
|
self.assertIsNone(conn.http_conn)
|
2013-10-17 11:12:06 +08:00
|
|
|
|
|
|
|
def test_close_ok(self):
|
|
|
|
url = 'http://www.test.com'
|
|
|
|
conn = c.Connection(url, 'asdf', 'asdf')
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(conn.http_conn)
|
2013-10-17 11:12:06 +08:00
|
|
|
conn.http_conn = c.http_connection(url)
|
2024-02-20 16:21:50 +11:00
|
|
|
self.assertIsInstance(conn.http_conn, tuple)
|
2013-10-17 11:12:06 +08:00
|
|
|
self.assertEqual(len(conn.http_conn), 2)
|
|
|
|
http_conn_obj = conn.http_conn[1]
|
2014-10-24 01:02:53 -07:00
|
|
|
self.assertIsInstance(http_conn_obj, c.HTTPConnection)
|
2018-06-22 16:49:03 -07:00
|
|
|
self.assertTrue(hasattr(http_conn_obj, 'close'))
|
2013-10-17 11:12:06 +08:00
|
|
|
conn.close()
|
2015-05-13 09:48:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestServiceToken(MockHttpTest):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
super(TestServiceToken, self).setUp()
|
|
|
|
self.os_options = {
|
|
|
|
'object_storage_url': 'http://storage_url.com',
|
|
|
|
'service_username': 'service_username',
|
|
|
|
'service_project_name': 'service_project_name',
|
|
|
|
'service_key': 'service_key'}
|
|
|
|
|
|
|
|
def get_connection(self):
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
os_options=self.os_options)
|
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIs(type(conn), c.Connection)
|
2015-05-13 09:48:41 +00:00
|
|
|
conn.get_auth = self.get_auth
|
|
|
|
conn.get_service_auth = self.get_service_auth
|
|
|
|
|
|
|
|
self.assertEqual(conn.attempts, 0)
|
2015-12-30 17:20:35 +05:30
|
|
|
self.assertIsNone(conn.service_token)
|
2015-05-13 09:48:41 +00:00
|
|
|
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertIs(type(conn), c.Connection)
|
2015-05-13 09:48:41 +00:00
|
|
|
return conn
|
|
|
|
|
|
|
|
def get_auth(self):
|
|
|
|
# The real get_auth function will always return the os_option
|
|
|
|
# dict's object_storage_url which will be overridden by the
|
2015-12-29 16:54:05 -08:00
|
|
|
# preauthurl parameter to Connection if it is provided.
|
2015-05-13 09:48:41 +00:00
|
|
|
return self.os_options.get('object_storage_url'), 'token'
|
|
|
|
|
|
|
|
def get_service_auth(self):
|
|
|
|
# The real get_auth function will always return the os_option
|
|
|
|
# dict's object_storage_url which will be overridden by the
|
|
|
|
# preauthurl parameter to Connection if it is provided.
|
|
|
|
return self.os_options.get('object_storage_url'), 'stoken'
|
|
|
|
|
|
|
|
def test_service_token_reauth(self):
|
|
|
|
get_auth_call_list = []
|
|
|
|
|
|
|
|
def get_auth(url, user, key, **kwargs):
|
|
|
|
# The real get_auth function will always return the os_option
|
|
|
|
# dict's object_storage_url which will be overridden by the
|
|
|
|
# preauthurl parameter to Connection if it is provided.
|
|
|
|
args = {'url': url, 'user': user, 'key': key, 'kwargs': kwargs}
|
|
|
|
get_auth_call_list.append(args)
|
|
|
|
return_dict = {'asdf': 'new', 'service_username': 'newserv'}
|
|
|
|
storage_url = kwargs['os_options'].get('object_storage_url')
|
|
|
|
return storage_url, return_dict[user]
|
|
|
|
|
|
|
|
def swap_sleep(*args):
|
|
|
|
self.swap_sleep_called = True
|
|
|
|
c.get_auth = get_auth
|
|
|
|
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(401, 200)):
|
|
|
|
with mock.patch('swiftclient.client.sleep', swap_sleep):
|
|
|
|
self.swap_sleep_called = False
|
|
|
|
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
preauthurl='http://www.old.com',
|
|
|
|
preauthtoken='old',
|
|
|
|
os_options=self.os_options)
|
|
|
|
|
|
|
|
self.assertEqual(conn.attempts, 0)
|
|
|
|
self.assertEqual(conn.url, 'http://www.old.com')
|
|
|
|
self.assertEqual(conn.token, 'old')
|
|
|
|
|
|
|
|
conn.head_account()
|
|
|
|
|
|
|
|
self.assertTrue(self.swap_sleep_called)
|
|
|
|
self.assertEqual(conn.attempts, 2)
|
|
|
|
# The original 'preauth' storage URL *must* be preserved
|
|
|
|
self.assertEqual(conn.url, 'http://www.old.com')
|
|
|
|
self.assertEqual(conn.token, 'new')
|
|
|
|
self.assertEqual(conn.service_token, 'newserv')
|
|
|
|
|
|
|
|
# Check get_auth was called with expected args
|
|
|
|
auth_args = get_auth_call_list[0]
|
|
|
|
auth_kwargs = get_auth_call_list[0]['kwargs']
|
|
|
|
self.assertEqual('asdf', auth_args['user'])
|
|
|
|
self.assertEqual('asdf', auth_args['key'])
|
|
|
|
self.assertEqual('service_key',
|
|
|
|
auth_kwargs['os_options']['service_key'])
|
|
|
|
self.assertEqual('service_username',
|
|
|
|
auth_kwargs['os_options']['service_username'])
|
|
|
|
self.assertEqual('service_project_name',
|
|
|
|
auth_kwargs['os_options']['service_project_name'])
|
|
|
|
|
|
|
|
auth_args = get_auth_call_list[1]
|
|
|
|
auth_kwargs = get_auth_call_list[1]['kwargs']
|
|
|
|
self.assertEqual('service_username', auth_args['user'])
|
|
|
|
self.assertEqual('service_key', auth_args['key'])
|
|
|
|
self.assertEqual('service_project_name',
|
|
|
|
auth_kwargs['os_options']['tenant_name'])
|
|
|
|
|
2016-06-07 15:49:32 +02:00
|
|
|
def test_service_token_reauth_retries_0(self):
|
|
|
|
get_auth_call_list = []
|
|
|
|
|
|
|
|
def get_auth(url, user, key, **kwargs):
|
|
|
|
# The real get_auth function will always return the os_option
|
|
|
|
# dict's object_storage_url which will be overridden by the
|
|
|
|
# preauthurl parameter to Connection if it is provided.
|
|
|
|
args = {'url': url, 'user': user, 'key': key, 'kwargs': kwargs}
|
|
|
|
get_auth_call_list.append(args)
|
|
|
|
return_dict = {'asdf': 'new', 'service_username': 'newserv'}
|
|
|
|
storage_url = kwargs['os_options'].get('object_storage_url')
|
|
|
|
return storage_url, return_dict[user]
|
|
|
|
|
|
|
|
def swap_sleep(*args):
|
|
|
|
self.swap_sleep_called = True
|
|
|
|
c.get_auth = get_auth
|
|
|
|
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(401, 200)):
|
|
|
|
with mock.patch('swiftclient.client.sleep', swap_sleep):
|
|
|
|
self.swap_sleep_called = False
|
|
|
|
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
preauthurl='http://www.old.com',
|
|
|
|
preauthtoken='old',
|
|
|
|
os_options=self.os_options,
|
|
|
|
retries=0)
|
|
|
|
|
|
|
|
self.assertEqual(conn.attempts, 0)
|
|
|
|
self.assertEqual(conn.url, 'http://www.old.com')
|
|
|
|
self.assertEqual(conn.token, 'old')
|
|
|
|
|
|
|
|
conn.head_account()
|
|
|
|
|
|
|
|
self.assertTrue(self.swap_sleep_called)
|
|
|
|
self.assertEqual(conn.attempts, 2)
|
|
|
|
# The original 'preauth' storage URL *must* be preserved
|
|
|
|
self.assertEqual(conn.url, 'http://www.old.com')
|
|
|
|
self.assertEqual(conn.token, 'new')
|
|
|
|
self.assertEqual(conn.service_token, 'newserv')
|
|
|
|
|
|
|
|
# Check get_auth was called with expected args
|
|
|
|
auth_args = get_auth_call_list[0]
|
|
|
|
auth_kwargs = get_auth_call_list[0]['kwargs']
|
|
|
|
self.assertEqual('asdf', auth_args['user'])
|
|
|
|
self.assertEqual('asdf', auth_args['key'])
|
|
|
|
self.assertEqual('service_key',
|
|
|
|
auth_kwargs['os_options']['service_key'])
|
|
|
|
self.assertEqual('service_username',
|
|
|
|
auth_kwargs['os_options']['service_username'])
|
|
|
|
self.assertEqual('service_project_name',
|
|
|
|
auth_kwargs['os_options']['service_project_name'])
|
|
|
|
|
|
|
|
auth_args = get_auth_call_list[1]
|
|
|
|
auth_kwargs = get_auth_call_list[1]['kwargs']
|
|
|
|
self.assertEqual('service_username', auth_args['user'])
|
|
|
|
self.assertEqual('service_key', auth_args['key'])
|
|
|
|
self.assertEqual('service_project_name',
|
|
|
|
auth_kwargs['os_options']['tenant_name'])
|
|
|
|
|
|
|
|
# Ensure this is not an endless loop - it fails after the second 401
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(401, 401, 401, 401)):
|
|
|
|
with mock.patch('swiftclient.client.sleep', swap_sleep):
|
|
|
|
self.swap_sleep_called = False
|
|
|
|
|
|
|
|
conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
|
|
|
|
preauthurl='http://www.old.com',
|
|
|
|
preauthtoken='old',
|
|
|
|
os_options=self.os_options,
|
|
|
|
retries=0)
|
|
|
|
|
|
|
|
self.assertEqual(conn.attempts, 0)
|
|
|
|
self.assertRaises(c.ClientException, conn.head_account)
|
|
|
|
self.assertEqual(conn.attempts, 2)
|
|
|
|
unused_responses = list(self.fake_connect.code_iter)
|
|
|
|
self.assertEqual(unused_responses, [401, 401])
|
|
|
|
|
2015-05-13 09:48:41 +00:00
|
|
|
def test_service_token_get_account(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
with mock.patch('swiftclient.client.parse_api_response'):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.get_account()
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('GET', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/?format=json',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_head_account(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.head_account()
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('HEAD', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com', actual['full_path'])
|
|
|
|
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_post_account(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(201)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.post_account(headers={})
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('POST', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com', actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_delete_container(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(204)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.delete_container('container1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('DELETE', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_get_container(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
with mock.patch('swiftclient.client.parse_api_response'):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.get_container('container1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('GET', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1?format=json',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
2016-02-22 15:05:27 +00:00
|
|
|
def test_service_token_get_container_full_listing(self):
|
|
|
|
# verify service token is sent with each request for a full listing
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200, 200)):
|
|
|
|
with mock.patch('swiftclient.client.parse_api_response') as resp:
|
|
|
|
resp.side_effect = ([{"name": "obj1"}], [])
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.get_container('container1', full_listing=True)
|
|
|
|
self.assertEqual(2, len(self.request_log), self.request_log)
|
|
|
|
expected_urls = iter((
|
|
|
|
'http://storage_url.com/container1?format=json',
|
|
|
|
'http://storage_url.com/container1?format=json&marker=obj1'
|
|
|
|
))
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('GET', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual(next(expected_urls),
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
2015-05-13 09:48:41 +00:00
|
|
|
def test_service_token_head_container(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.head_container('container1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('HEAD', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_post_container(self):
|
2017-08-25 12:13:12 -07:00
|
|
|
headers = {'X-Container-Meta-Color': 'blue'}
|
2015-05-13 09:48:41 +00:00
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(201)):
|
|
|
|
conn = self.get_connection()
|
2017-08-25 12:13:12 -07:00
|
|
|
conn.post_container('container1', headers)
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('POST', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
2017-08-25 12:13:12 -07:00
|
|
|
# Check that we didn't mutate the request header dict
|
|
|
|
self.assertEqual(headers, {'X-Container-Meta-Color': 'blue'})
|
2015-05-13 09:48:41 +00:00
|
|
|
|
|
|
|
def test_service_token_put_container(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.put_container('container1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('PUT', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_get_object(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.get_object('container1', 'obj1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('GET', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1/obj1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_head_object(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.head_object('container1', 'obj1')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('HEAD', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1/obj1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_put_object(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(200)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.put_object('container1', 'obj1', 'a_string')
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('PUT', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1/obj1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_post_object(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(202)):
|
|
|
|
conn = self.get_connection()
|
|
|
|
conn.post_object('container1', 'obj1', {})
|
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('POST', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1/obj1',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|
|
|
|
|
|
|
|
def test_service_token_delete_object(self):
|
|
|
|
with mock.patch('swiftclient.client.http_connection',
|
|
|
|
self.fake_http_connection(202)):
|
|
|
|
conn = self.get_connection()
|
2016-09-19 23:18:18 +08:00
|
|
|
conn.delete_object('container1', 'obj1', query_string='a_string')
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual(1, len(self.request_log), self.request_log)
|
|
|
|
for actual in self.iter_request_log():
|
|
|
|
self.assertEqual('DELETE', actual['method'])
|
|
|
|
actual_hdrs = actual['headers']
|
2015-09-09 17:41:21 -07:00
|
|
|
self.assertEqual('stoken', actual_hdrs.get('X-Service-Token'))
|
2015-05-13 09:48:41 +00:00
|
|
|
self.assertEqual('token', actual_hdrs['X-Auth-Token'])
|
|
|
|
self.assertEqual('http://storage_url.com/container1/obj1?a_string',
|
|
|
|
actual['full_path'])
|
|
|
|
self.assertEqual(conn.attempts, 1)
|