Skip to content

Commit 85b8a21

Browse files
committed
Move storage table to cosmos table
1 parent c530518 commit 85b8a21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+4241
-659
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,6 @@ test_results/
7777

7878
# Code coverage
7979
.coverage
80+
81+
# vscode
82+
.vscode/

azure/multiapi/cosmosdb/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__import__('pkg_resources').declare_namespace(__name__)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__import__('pkg_resources').declare_namespace(__name__)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# --------------------------------------------------------------------------
15+
from ._constants import (
16+
__author__,
17+
__version__,
18+
X_MS_VERSION,
19+
)
20+
from .cloudstorageaccount import CloudStorageAccount
21+
from .models import (
22+
RetentionPolicy,
23+
Logging,
24+
Metrics,
25+
CorsRule,
26+
ServiceProperties,
27+
AccessPolicy,
28+
ResourceTypes,
29+
Services,
30+
AccountPermissions,
31+
Protocol,
32+
ServiceStats,
33+
GeoReplication,
34+
LocationMode,
35+
RetryContext,
36+
)
37+
from .retry import (
38+
ExponentialRetry,
39+
LinearRetry,
40+
no_retry,
41+
)
42+
from .sharedaccesssignature import (
43+
SharedAccessSignature,
44+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# --------------------------------------------------------------------------
15+
from ._common_conversion import (
16+
_sign_string,
17+
)
18+
19+
import logging
20+
logger = logging.getLogger(__name__)
21+
22+
23+
class _StorageSharedKeyAuthentication(object):
24+
def __init__(self, account_name, account_key):
25+
self.account_name = account_name
26+
self.account_key = account_key
27+
28+
def _get_headers(self, request, headers_to_sign):
29+
headers = dict((name.lower(), value) for name, value in request.headers.items() if value)
30+
if 'content-length' in headers and headers['content-length'] == '0':
31+
del headers['content-length']
32+
return '\n'.join(headers.get(x, '') for x in headers_to_sign) + '\n'
33+
34+
def _get_verb(self, request):
35+
return request.method + '\n'
36+
37+
def _get_canonicalized_resource(self, request):
38+
uri_path = request.path.split('?')[0]
39+
return '/' + self.account_name + uri_path
40+
41+
def _get_canonicalized_headers(self, request):
42+
string_to_sign = ''
43+
x_ms_headers = []
44+
for name, value in request.headers.items():
45+
if name.startswith('x-ms-'):
46+
x_ms_headers.append((name.lower(), value))
47+
x_ms_headers.sort()
48+
for name, value in x_ms_headers:
49+
if value is not None:
50+
string_to_sign += ''.join([name, ':', value, '\n'])
51+
return string_to_sign
52+
53+
def _add_authorization_header(self, request, string_to_sign):
54+
signature = _sign_string(self.account_key, string_to_sign)
55+
auth_string = 'SharedKey ' + self.account_name + ':' + signature
56+
request.headers['Authorization'] = auth_string
57+
58+
59+
class _StorageSharedKeyAuthentication(_StorageSharedKeyAuthentication):
60+
def sign_request(self, request):
61+
string_to_sign = \
62+
self._get_verb(request) + \
63+
self._get_headers(
64+
request,
65+
[
66+
'content-encoding', 'content-language', 'content-length',
67+
'content-md5', 'content-type', 'date', 'if-modified-since',
68+
'if-match', 'if-none-match', 'if-unmodified-since', 'byte_range'
69+
]
70+
) + \
71+
self._get_canonicalized_headers(request) + \
72+
self._get_canonicalized_resource(request) + \
73+
self._get_canonicalized_resource_query(request)
74+
75+
self._add_authorization_header(request, string_to_sign)
76+
logger.debug("String_to_sign=%s", string_to_sign)
77+
78+
def _get_canonicalized_resource_query(self, request):
79+
sorted_queries = [(name, value) for name, value in request.query.items()]
80+
sorted_queries.sort()
81+
82+
string_to_sign = ''
83+
for name, value in sorted_queries:
84+
if value:
85+
string_to_sign += '\n' + name.lower() + ':' + value
86+
87+
return string_to_sign
88+
89+
90+
class _StorageTableSharedKeyAuthentication(_StorageSharedKeyAuthentication):
91+
def sign_request(self, request):
92+
string_to_sign = \
93+
self._get_verb(request) + \
94+
self._get_headers(
95+
request,
96+
['content-md5', 'content-type', 'x-ms-date'],
97+
) + \
98+
self._get_canonicalized_resource(request) + \
99+
self._get_canonicalized_resource_query(request)
100+
101+
self._add_authorization_header(request, string_to_sign)
102+
logger.debug("String_to_sign=%s", string_to_sign)
103+
104+
def _get_canonicalized_resource_query(self, request):
105+
for name, value in request.query.items():
106+
if name == 'comp':
107+
return '?comp=' + value
108+
return ''
109+
110+
111+
class _StorageNoAuthentication(object):
112+
def sign_request(self, request):
113+
pass
114+
115+
116+
class _StorageSASAuthentication(object):
117+
def __init__(self, sas_token):
118+
self.sas_token = sas_token
119+
120+
def sign_request(self, request):
121+
# if 'sig=' is present, then the request has already been signed
122+
# as is the case when performing retries
123+
if 'sig=' in request.path:
124+
return
125+
if '?' in request.path:
126+
request.path += '&'
127+
else:
128+
request.path += '?'
129+
130+
request.path += self.sas_token
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# --------------------------------------------------------------------------
15+
16+
import base64
17+
import hashlib
18+
import hmac
19+
import sys
20+
from io import (SEEK_SET)
21+
22+
from dateutil.tz import tzutc
23+
24+
from ._error import (
25+
_ERROR_VALUE_SHOULD_BE_BYTES_OR_STREAM,
26+
_ERROR_VALUE_SHOULD_BE_SEEKABLE_STREAM,
27+
)
28+
from .models import (
29+
_unicode_type,
30+
)
31+
32+
if sys.version_info < (3,):
33+
def _str(value):
34+
if isinstance(value, unicode):
35+
return value.encode('utf-8')
36+
37+
return str(value)
38+
else:
39+
_str = str
40+
41+
42+
def _to_str(value):
43+
return _str(value) if value is not None else None
44+
45+
46+
def _int_to_str(value):
47+
return str(int(value)) if value is not None else None
48+
49+
50+
def _bool_to_str(value):
51+
if value is None:
52+
return None
53+
54+
if isinstance(value, bool):
55+
if value:
56+
return 'true'
57+
else:
58+
return 'false'
59+
60+
return str(value)
61+
62+
63+
def _to_utc_datetime(value):
64+
return value.strftime('%Y-%m-%dT%H:%M:%SZ')
65+
66+
67+
def _datetime_to_utc_string(value):
68+
# Azure expects the date value passed in to be UTC.
69+
# Azure will always return values as UTC.
70+
# If a date is passed in without timezone info, it is assumed to be UTC.
71+
if value is None:
72+
return None
73+
74+
if value.tzinfo:
75+
value = value.astimezone(tzutc())
76+
77+
return value.strftime('%a, %d %b %Y %H:%M:%S GMT')
78+
79+
80+
def _encode_base64(data):
81+
if isinstance(data, _unicode_type):
82+
data = data.encode('utf-8')
83+
encoded = base64.b64encode(data)
84+
return encoded.decode('utf-8')
85+
86+
87+
def _decode_base64_to_bytes(data):
88+
if isinstance(data, _unicode_type):
89+
data = data.encode('utf-8')
90+
return base64.b64decode(data)
91+
92+
93+
def _decode_base64_to_text(data):
94+
decoded_bytes = _decode_base64_to_bytes(data)
95+
return decoded_bytes.decode('utf-8')
96+
97+
98+
def _sign_string(key, string_to_sign, key_is_base64=True):
99+
if key_is_base64:
100+
key = _decode_base64_to_bytes(key)
101+
else:
102+
if isinstance(key, _unicode_type):
103+
key = key.encode('utf-8')
104+
if isinstance(string_to_sign, _unicode_type):
105+
string_to_sign = string_to_sign.encode('utf-8')
106+
signed_hmac_sha256 = hmac.HMAC(key, string_to_sign, hashlib.sha256)
107+
digest = signed_hmac_sha256.digest()
108+
encoded_digest = _encode_base64(digest)
109+
return encoded_digest
110+
111+
112+
def _get_content_md5(data):
113+
md5 = hashlib.md5()
114+
if isinstance(data, bytes):
115+
md5.update(data)
116+
elif hasattr(data, 'read'):
117+
pos = 0
118+
try:
119+
pos = data.tell()
120+
except:
121+
pass
122+
for chunk in iter(lambda: data.read(4096), b""):
123+
md5.update(chunk)
124+
try:
125+
data.seek(pos, SEEK_SET)
126+
except (AttributeError, IOError):
127+
raise ValueError(_ERROR_VALUE_SHOULD_BE_SEEKABLE_STREAM.format('data'))
128+
else:
129+
raise ValueError(_ERROR_VALUE_SHOULD_BE_BYTES_OR_STREAM.format('data'))
130+
131+
return base64.b64encode(md5.digest()).decode('utf-8')
132+
133+
134+
def _lower(text):
135+
return text.lower()

0 commit comments

Comments
 (0)