Skip to content

Commit a5680f0

Browse files
authoredMay 21, 2018
Merge pull request #73 from mattsb42-aws/fix-72
Fix bugs identified in #72, #74, and #75
2 parents 4482bf4 + 3bed103 commit a5680f0

File tree

9 files changed

+233
-14
lines changed

9 files changed

+233
-14
lines changed
 

‎CHANGELOG.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,30 @@
22
Changelog
33
*********
44

5+
1.0.4 -- 2018-05-22
6+
===================
7+
8+
Bugfixes
9+
--------
10+
* Fix :class:`MostRecentProvider` behavior when lock cannot be acquired.
11+
`#72 <https://github.com/awslabs/aws-dynamodb-encryption-python/issues/72>`_
12+
* Fix :class:`MostRecentProvider` lock acquisition for Python 2.7.
13+
`#74 <https://github.com/awslabs/aws-dynamodb-encryption-python/issues/74>`_
14+
* Fix :class:`TableInfo` secondary index storage.
15+
`#75 <https://github.com/awslabs/aws-dynamodb-encryption-python/issues/75>`_
16+
517
1.0.3 -- 2018-05-03
618
===================
19+
20+
Bugfixes
21+
--------
722
* Finish fixing ``MANIFEST.in``.
823

924
1.0.2 -- 2018-05-03
1025
===================
26+
27+
Bugfixes
28+
--------
1129
* Fill out ``MANIFEST.in`` to correctly include necessary files in source build.
1230

1331
1.0.1 -- 2018-05-02

‎src/dynamodb_encryption_sdk/identifiers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from enum import Enum
1515

1616
__all__ = ('LOGGER_NAME', 'CryptoAction', 'EncryptionKeyType', 'KeyEncodingType')
17-
__version__ = '1.0.3'
17+
__version__ = '1.0.4'
1818

1919
LOGGER_NAME = 'dynamodb_encryption_sdk'
2020
USER_AGENT_SUFFIX = 'DynamodbEncryptionSdkPython/{}'.format(__version__)

‎src/dynamodb_encryption_sdk/material_providers/most_recent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def decryption_materials(self, encryption_context):
182182
return provider.decryption_materials(encryption_context)
183183

184184
def _ttl_action(self):
185-
# type: () -> bool
185+
# type: () -> TtlActions
186186
"""Determine the correct action to take based on the local resources and TTL.
187187
188188
:returns: decision
@@ -240,14 +240,14 @@ def _get_most_recent_version(self, allow_local):
240240
:returns: version and corresponding cryptographic materials provider
241241
:rtype: CryptographicMaterialsProvider
242242
"""
243-
acquired = self._lock.acquire(blocking=not allow_local)
243+
acquired = self._lock.acquire(not allow_local)
244244

245245
if not acquired:
246246
# We failed to acquire the lock.
247247
# If blocking, we will never reach this point.
248248
# If not blocking, we want whatever the latest local version is.
249249
version = self._version
250-
return version, self._cache.get(version)
250+
return self._cache.get(version)
251251

252252
try:
253253
max_version = self._get_max_version()

‎src/dynamodb_encryption_sdk/structures.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import six
1818

1919
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
20-
from typing import Dict, Iterable, Optional, Set, Text # noqa pylint: disable=unused-import
20+
from typing import Dict, Iterable, List, Optional, Set, Text # noqa pylint: disable=unused-import
2121
except ImportError: # pragma: no cover
2222
# We only actually need these imports when running the mypy checks
2323
pass
@@ -295,7 +295,7 @@ class TableInfo(object):
295295
:param bool all_encrypting_secondary_indexes: Should we allow secondary index attributes to be encrypted?
296296
:param TableIndex primary_index: Description of primary index
297297
:param secondary_indexes: Set of TableIndex objects describing any secondary indexes
298-
:type secondary_indexes: set(TableIndex)
298+
:type secondary_indexes: list(TableIndex)
299299
"""
300300

301301
name = attr.ib(validator=attr.validators.instance_of(six.string_types))
@@ -304,15 +304,15 @@ class TableInfo(object):
304304
default=None
305305
)
306306
_secondary_indexes = attr.ib(
307-
validator=attr.validators.optional(iterable_validator(set, TableIndex)),
307+
validator=attr.validators.optional(iterable_validator(list, TableIndex)),
308308
default=None
309309
)
310310

311311
def __init__(
312312
self,
313313
name, # type: Text
314314
primary_index=None, # type: Optional[TableIndex]
315-
secondary_indexes=None # type: Optional[Set[TableIndex]]
315+
secondary_indexes=None # type: Optional[List[TableIndex]]
316316
): # noqa=D107
317317
# type: (...) -> None
318318
# Workaround pending resolution of attrs/mypy interaction.
@@ -338,7 +338,7 @@ def primary_index(self):
338338

339339
@property
340340
def secondary_indexes(self):
341-
# type: () -> Set[TableIndex]
341+
# type: () -> List[TableIndex]
342342
"""Return the primary TableIndex.
343343
344344
:returns: secondary index descriptions
@@ -378,10 +378,10 @@ def refresh_indexed_attributes(self, client):
378378
table = client.describe_table(TableName=self.name)['Table']
379379
self._primary_index = TableIndex.from_key_schema(table['KeySchema'])
380380

381-
self._secondary_indexes = set()
381+
self._secondary_indexes = []
382382
for group in ('LocalSecondaryIndexes', 'GlobalSecondaryIndexes'):
383383
try:
384384
for index in table[group]:
385-
self._secondary_indexes.add(TableIndex.from_key_schema(index['KeySchema']))
385+
self._secondary_indexes.append(TableIndex.from_key_schema(index['KeySchema']))
386386
except KeyError:
387387
pass # Not all tables will have secondary indexes.

‎test/functional/functional_test_utils.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@
4848
'value': Decimal('99.233')
4949
}
5050
}
51+
SECONARY_INDEX = {
52+
'secondary_index_1': {
53+
'type': 'B',
54+
'value': Binary(b'\x00\x01\x02')
55+
},
56+
'secondary_index_1': {
57+
'type': 'S',
58+
'value': 'another_value'
59+
}
60+
}
5161
TEST_KEY = {name: value['value'] for name, value in TEST_INDEX.items()}
5262
TEST_BATCH_INDEXES = [
5363
{
@@ -130,6 +140,132 @@ def example_table():
130140
mock_dynamodb2().stop()
131141

132142

143+
@pytest.fixture
144+
def table_with_local_seconary_indexes():
145+
mock_dynamodb2().start()
146+
ddb = boto3.client('dynamodb', region_name='us-west-2')
147+
ddb.create_table(
148+
TableName=TEST_TABLE_NAME,
149+
KeySchema=[
150+
{
151+
'AttributeName': 'partition_attribute',
152+
'KeyType': 'HASH'
153+
},
154+
{
155+
'AttributeName': 'sort_attribute',
156+
'KeyType': 'RANGE'
157+
}
158+
],
159+
LocalSecondaryIndexes=[
160+
{
161+
'IndexName': 'lsi-1',
162+
'KeySchema': [
163+
{
164+
'AttributeName': 'secondary_index_1',
165+
'KeyType': 'HASH'
166+
}
167+
],
168+
'Projection': {
169+
'ProjectionType': 'ALL'
170+
}
171+
},
172+
{
173+
'IndexName': 'lsi-2',
174+
'KeySchema': [
175+
{
176+
'AttributeName': 'secondary_index_2',
177+
'KeyType': 'HASH'
178+
}
179+
],
180+
'Projection': {
181+
'ProjectionType': 'ALL'
182+
}
183+
}
184+
],
185+
AttributeDefinitions=[
186+
{
187+
'AttributeName': name,
188+
'AttributeType': value['type']
189+
}
190+
for name, value in list(TEST_INDEX.items()) + list(SECONARY_INDEX.items())
191+
],
192+
ProvisionedThroughput={
193+
'ReadCapacityUnits': 100,
194+
'WriteCapacityUnits': 100
195+
}
196+
)
197+
yield
198+
ddb.delete_table(TableName=TEST_TABLE_NAME)
199+
mock_dynamodb2().stop()
200+
201+
202+
@pytest.fixture
203+
def table_with_global_seconary_indexes():
204+
mock_dynamodb2().start()
205+
ddb = boto3.client('dynamodb', region_name='us-west-2')
206+
ddb.create_table(
207+
TableName=TEST_TABLE_NAME,
208+
KeySchema=[
209+
{
210+
'AttributeName': 'partition_attribute',
211+
'KeyType': 'HASH'
212+
},
213+
{
214+
'AttributeName': 'sort_attribute',
215+
'KeyType': 'RANGE'
216+
}
217+
],
218+
GlobalSecondaryIndexes=[
219+
{
220+
'IndexName': 'gsi-1',
221+
'KeySchema': [
222+
{
223+
'AttributeName': 'secondary_index_1',
224+
'KeyType': 'HASH'
225+
}
226+
],
227+
'Projection': {
228+
'ProjectionType': 'ALL'
229+
},
230+
'ProvisionedThroughput': {
231+
'ReadCapacityUnits': 100,
232+
'WriteCapacityUnits': 100
233+
}
234+
},
235+
{
236+
'IndexName': 'gsi-2',
237+
'KeySchema': [
238+
{
239+
'AttributeName': 'secondary_index_2',
240+
'KeyType': 'HASH'
241+
}
242+
],
243+
'Projection': {
244+
'ProjectionType': 'ALL'
245+
},
246+
'ProvisionedThroughput': {
247+
'ReadCapacityUnits': 100,
248+
'WriteCapacityUnits': 100
249+
}
250+
}
251+
],
252+
AttributeDefinitions=[
253+
{
254+
'AttributeName': name,
255+
'AttributeType': value['type']
256+
}
257+
for name, value in list(TEST_INDEX.items()) + list(SECONARY_INDEX.items())
258+
],
259+
ProvisionedThroughput={
260+
'ReadCapacityUnits': 100,
261+
'WriteCapacityUnits': 100
262+
}
263+
)
264+
yield
265+
ddb.delete_table(TableName=TEST_TABLE_NAME)
266+
mock_dynamodb2().stop()
267+
268+
133269
def _get_from_cache(dk_class, algorithm, key_length):
134270
"""Don't generate new keys every time. All we care about is that they are valid keys, not that they are unique."""
135271
try:

‎test/functional/material_providers/store/test_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
1111
# ANY KIND, either express or implied. See the License for the specific
1212
# language governing permissions and limitations under the License.
13-
"""Integration tests for ``dynamodb_encryption_sdk.material_providers.store.meta``."""
13+
"""Functional tests for ``dynamodb_encryption_sdk.material_providers.store.meta``."""
1414
import base64
1515
import os
1616

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""Functional tests for ``dynamodb_encryption_sdk.material_providers.most_recent``."""
14+
from mock import MagicMock, sentinel
15+
import pytest
16+
17+
from dynamodb_encryption_sdk.material_providers.most_recent import MostRecentProvider
18+
from dynamodb_encryption_sdk.material_providers.store import ProviderStore
19+
20+
pytestmark = [pytest.mark.functional, pytest.mark.local]
21+
22+
23+
def test_failed_lock_acquisition():
24+
store = MagicMock(__class__=ProviderStore)
25+
provider = MostRecentProvider(
26+
provider_store=store,
27+
material_name='my material',
28+
version_ttl=10.0
29+
)
30+
provider._version = 9
31+
provider._cache.put(provider._version, sentinel.nine)
32+
33+
with provider._lock:
34+
test = provider._get_most_recent_version(allow_local=True)
35+
36+
assert test is sentinel.nine
37+
assert not store.mock_calls

‎test/functional/test_structures.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,42 @@
1111
# ANY KIND, either express or implied. See the License for the specific
1212
# language governing permissions and limitations under the License.
1313
"""Functional tests for ``dynamodb_encryption_sdk.structures``."""
14+
import boto3
1415
import pytest
1516

1617
from dynamodb_encryption_sdk.exceptions import InvalidArgumentError
1718
from dynamodb_encryption_sdk.identifiers import CryptoAction
18-
from dynamodb_encryption_sdk.structures import AttributeActions, TableIndex
19+
from dynamodb_encryption_sdk.structures import AttributeActions, TableIndex, TableInfo
20+
from .functional_test_utils import (
21+
example_table, table_with_global_seconary_indexes, table_with_local_seconary_indexes, TEST_TABLE_NAME
22+
)
1923

2024
pytestmark = [pytest.mark.functional, pytest.mark.local]
2125

2226

27+
# TODO: There is a way to parameterize fixtures, but the existing docs on that are very unclear.
28+
# This will get us what we need for now, but we should come back to this to clean this up later.
29+
def test_tableinfo_refresh_indexes_no_secondary_indexes(example_table):
30+
client = boto3.client('dynamodb', region_name='us-west-2')
31+
table = TableInfo(name=TEST_TABLE_NAME)
32+
33+
table.refresh_indexed_attributes(client)
34+
35+
36+
def test_tableinfo_refresh_indexes_with_gsis(table_with_global_seconary_indexes):
37+
client = boto3.client('dynamodb', region_name='us-west-2')
38+
table = TableInfo(name=TEST_TABLE_NAME)
39+
40+
table.refresh_indexed_attributes(client)
41+
42+
43+
def test_tableinfo_refresh_indexes_with_lsis(table_with_local_seconary_indexes):
44+
client = boto3.client('dynamodb', region_name='us-west-2')
45+
table = TableInfo(name=TEST_TABLE_NAME)
46+
47+
table.refresh_indexed_attributes(client)
48+
49+
2350
@pytest.mark.parametrize('kwargs, expected_attributes', (
2451
(dict(partition='partition_name'), set(['partition_name'])),
2552
(dict(partition='partition_name', sort='sort_name'), set(['partition_name', 'sort_name']))

‎tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ commands =
259259
basepython = python3
260260
deps = -rdoc/requirements.txt
261261
commands =
262-
sphinx-build -E -c doc/ -b html -b linkcheck doc/ doc/build/html
262+
sphinx-build -E -c doc/ -b html doc/ doc/build/html
263+
sphinx-build -E -c doc/ -b linkcheck doc/ doc/build/html
263264

264265
[testenv:serve-docs]
265266
basepython = python3

0 commit comments

Comments
 (0)
Please sign in to comment.