def describe_table(self, context, table_name): table_info = self._table_info_repo.get( context, table_name, ['status', 'last_update_date_time']) if timeutils.is_older_than(table_info.last_update_date_time, self._schema_operation_timeout): if table_info.status == models.TableMeta.TABLE_STATUS_CREATING: table_info.status = models.TableMeta.TABLE_STATUS_CREATE_FAILED self._table_info_repo.update(context, table_info, ['status']) LOG.debug("Table '{}' creation timed out." " Setting status to {}".format( table_info.name, models.TableMeta.TABLE_STATUS_CREATE_FAILED)) if table_info.status == models.TableMeta.TABLE_STATUS_DELETING: table_info.status = models.TableMeta.TABLE_STATUS_DELETE_FAILED self._table_info_repo.update(context, table_info, ['status']) LOG.debug("Table '{}' deletion timed out." " Setting status to {}".format( table_info.name, models.TableMeta.TABLE_STATUS_DELETE_FAILED)) return models.TableMeta(table_info.id, table_info.schema, table_info.status, table_info.creation_date_time)
def test_describe_table(self): self.storage_mocker.StubOutWithMock(storage, 'describe_table') storage.describe_table(IgnoreArg(), 'test_table').AndReturn( models.TableMeta( models.TableSchema( { 'city1': models.ATTRIBUTE_TYPE_STRING, 'id': models.ATTRIBUTE_TYPE_STRING, 'name': models.ATTRIBUTE_TYPE_STRING }, ['id', 'name'], {'index_name': models.IndexDefinition('city1')}), models.TableMeta.TABLE_STATUS_ACTIVE)) self.storage_mocker.ReplayAll() table = Table('test_table', connection=self.DYNAMODB_CON) table_description = table.describe() self.storage_mocker.VerifyAll() self.assertEquals('test_table', table_description['Table']['TableName']) self.assertItemsEqual([{ "AttributeName": "city1", "AttributeType": "S" }, { "AttributeName": "id", "AttributeType": "S" }, { "AttributeName": "name", "AttributeType": "S" }], table_description['Table']['AttributeDefinitions'])
def test_create_table_no_range(self): self.storage_mocker.StubOutWithMock(storage, 'create_table') storage.create_table(IgnoreArg(), IgnoreArg(), IgnoreArg()).AndReturn( models.TableMeta( models.TableSchema( { 'hash': models.ATTRIBUTE_TYPE_NUMBER, 'indexed_field': models.ATTRIBUTE_TYPE_STRING }, ['hash'], {"index_name": models.IndexDefinition('indexed_field')}), models.TableMeta.TABLE_STATUS_ACTIVE)) self.storage_mocker.ReplayAll() Table.create("test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.RangeKey('indexed_field', data_type=schema_types.STRING) ]) ], connection=self.DYNAMODB_CON) self.storage_mocker.VerifyAll()
def test_update_item(self): self.storage_mocker.StubOutWithMock(storage, 'get_item') hash_key = "4.5621201231232132132132132132132142354E126" range_key = "range" storage.get_item( mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), select_type=mox.IgnoreArg(), consistent=mox.IgnoreArg() ).AndReturn( models.SelectResult( items=[ { "hash_key": models.AttributeValue('N', hash_key), "range_key": models.AttributeValue('S', range_key), "attr_value": models.AttributeValue('S', 'val') } ] ) ) self.storage_mocker.StubOutWithMock(storage, 'describe_table') storage.describe_table(mox.IgnoreArg(), 'test_table').AndReturn( models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema( { 'hash_key': models.AttributeType('N'), 'range_key': models.AttributeType('S') }, ['hash_key', 'range_key'], ), models.TableMeta.TABLE_STATUS_ACTIVE, None ) ) self.storage_mocker.StubOutWithMock(storage, 'update_item') storage.update_item( mox.IgnoreArg(), mox.IgnoreArg(), key_attribute_map=mox.IgnoreArg(), attribute_action_map=mox.IgnoreArg(), expected_condition_map=mox.IgnoreArg()).AndReturn((True, None)) self.storage_mocker.ReplayAll() table = ddb_table.Table( 'test_table', connection=self.DYNAMODB_CON ) item = table.get_item(consistent=False, hash_key=1, range_key="range") item['attr_value'] = 'updated' item.partial_save() self.storage_mocker.VerifyAll()
def test_delete_table(self): self.storage_mocker.StubOutWithMock(storage, 'delete_table') self.storage_mocker.StubOutWithMock(storage, 'describe_table') storage.delete_table(mox.IgnoreArg(), 'test_table') storage.describe_table(mox.IgnoreArg(), 'test_table').AndReturn( models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema( { 'city1': models.AttributeType('S'), 'id': models.AttributeType('S'), 'name': models.AttributeType('S') }, ['id', 'name'], {'index_name': models.IndexDefinition('id', 'city1')} ), models.TableMeta.TABLE_STATUS_ACTIVE, None ) ) self.storage_mocker.ReplayAll() table = ddb_table.Table( 'test_table', connection=self.DYNAMODB_CON ) self.assertTrue(table.delete()) self.storage_mocker.VerifyAll()
def delete_table(self, context, table_name): try: table_info = self._table_info_repo.get(context, table_name, ['status']) except exception.TableNotExistsException: raise if table_info.status == models.TableMeta.TABLE_STATUS_DELETING: # table is already being deleted, just return immediately return models.TableMeta(table_info.id, table_info.schema, table_info.status, table_info.creation_date_time) elif table_info.in_use: raise exception.ResourceInUseException() table_info.status = models.TableMeta.TABLE_STATUS_DELETING self._table_info_repo.update(context, table_info, ["status"]) if not table_info.internal_name: # if table internal name is missing, table is not actually created # just remove the table_info entry for the table and # send notification msg = ("Table '{}' with tenant id '{}', id '{}' does not have " "valid internal name. Unable or no need to delete.").format( table_info.name, context.tenant, table_info.id) LOG.info(msg) self._table_info_repo.delete(context, table_info.name) self._notifier.info( context, notifier.EVENT_TYPE_TABLE_DELETE, dict(table_name=table_name, message=msg, value=time.time())) else: self._do_delete_table(context, table_info) return models.TableMeta(table_info.id, table_info.schema, table_info.status, table_info.creation_date_time)
def test_update_item(self): self.storage_mocker.StubOutWithMock(storage, 'select_item') hash_key = "4.5621201231232132132132132132132142354E126" range_key = "range" storage.select_item( IgnoreArg(), IgnoreArg(), IgnoreArg(), select_type=IgnoreArg(), limit=IgnoreArg(), consistent=IgnoreArg()).AndReturn( models.SelectResult( items=[{ "hash_key": models.AttributeValue.number(hash_key), "range_key": models.AttributeValue.str(range_key), "attr_value": models.AttributeValue.str('val') }])) self.storage_mocker.StubOutWithMock(storage, 'describe_table') storage.describe_table(IgnoreArg(), 'test_table').AndReturn( models.TableMeta( models.TableSchema( { 'hash_key': models.ATTRIBUTE_TYPE_NUMBER, 'range_key': models.ATTRIBUTE_TYPE_STRING }, ['hash_key', 'range_key'], ), models.TableMeta.TABLE_STATUS_ACTIVE)) self.storage_mocker.StubOutWithMock(storage, 'update_item') storage.update_item(IgnoreArg(), IgnoreArg(), key_attribute_map=IgnoreArg(), attribute_action_map=IgnoreArg(), expected_condition_map=IgnoreArg()).AndReturn(True) self.storage_mocker.ReplayAll() table = Table('test_table', connection=self.DYNAMODB_CON) item = table.get_item(consistent=False, hash_key=1, range_key="range") item['attr_value'] = 'updated' item.partial_save() self.storage_mocker.VerifyAll()
def test_create_table(self): self.storage_mocker.StubOutWithMock(storage, 'create_table') storage.create_table( mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg() ).AndReturn( models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema( { 'hash': models.AttributeType('N'), 'range': models.AttributeType('S'), 'indexed_field': models.AttributeType('S') }, ['hash', 'range'], { "index_name": models.IndexDefinition('hash', 'indexed_field') } ), models.TableMeta.TABLE_STATUS_ACTIVE, None ) ) self.storage_mocker.ReplayAll() ddb_table.Table.create( "test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('range', data_type=schema_types.STRING) ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('indexed_field', data_type=schema_types.STRING) ] ) ], connection=self.DYNAMODB_CON ) self.storage_mocker.VerifyAll()
def create_table(self, context, table_name, table_schema): table_id = self._get_table_id(table_name) table_info = table_info_repo.TableInfo( table_name, table_id, table_schema, models.TableMeta.TABLE_STATUS_CREATING) try: self._table_info_repo.save(context, table_info) except exception.TableAlreadyExistsException: raise self._do_create_table(context, table_info) return models.TableMeta(table_info.id, table_info.schema, table_info.status, table_info.creation_date_time)
def test_delete_table(self): self.storage_mocker.StubOutWithMock(storage, 'delete_table') self.storage_mocker.StubOutWithMock(storage, 'describe_table') storage.delete_table(IgnoreArg(), 'test_table') storage.describe_table(IgnoreArg(), 'test_table').AndReturn( models.TableMeta( models.TableSchema( { 'city1': models.ATTRIBUTE_TYPE_STRING, 'id': models.ATTRIBUTE_TYPE_STRING, 'name': models.ATTRIBUTE_TYPE_STRING }, ['id', 'name'], {'index_name': models.IndexDefinition('city1')}), models.TableMeta.TABLE_STATUS_ACTIVE)) self.storage_mocker.ReplayAll() table = Table('test_table', connection=self.DYNAMODB_CON) self.assertTrue(table.delete()) self.storage_mocker.VerifyAll()
def test_describe_table(self, mock_describe_table): attr_map = { 'ForumName': models.AttributeType('S'), 'Subject': models.AttributeType('S'), 'LastPostDateTime': models.AttributeType('S') } key_attrs = ['ForumName', 'Subject'] index_map = { 'LastPostIndex': models.IndexDefinition('ForumName', 'LastPostDateTime') } table_meta = models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema(attr_map, key_attrs, index_map), models.TableMeta.TABLE_STATUS_ACTIVE, 123) mock_describe_table.return_value = table_meta headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } conn = httplib.HTTPConnection('localhost:8080') url = '/v1/data/default_tenant/tables/Thread' table_url = ('http://localhost:8080/v1/data/default_tenant' '/tables/Thread') expected_response = { 'table': { 'attribute_definitions': [{ 'attribute_name': 'Subject', 'attribute_type': 'S' }, { 'attribute_name': 'LastPostDateTime', 'attribute_type': 'S' }, { 'attribute_name': 'ForumName', 'attribute_type': 'S' }], 'creation_date_time': 123, 'item_count': 0, 'key_schema': [{ 'attribute_name': 'ForumName', 'key_type': 'HASH' }, { 'attribute_name': 'Subject', 'key_type': 'RANGE' }], 'local_secondary_indexes': [{ 'index_name': 'LastPostIndex', 'index_size_bytes': 0, 'item_count': 0, 'key_schema': [{ 'attribute_name': 'ForumName', 'key_type': 'HASH' }, { 'attribute_name': 'LastPostDateTime', 'key_type': 'RANGE' }], 'projection': { 'projection_type': 'ALL' } }], 'table_id': '00000000-0000-0000-0000-000000000000', 'table_name': 'Thread', 'table_size_bytes': 0, 'table_status': 'ACTIVE', 'links': [{ 'href': table_url, 'rel': 'self' }, { 'href': table_url, 'rel': 'bookmark' }] } } conn.request("GET", url, headers=headers) response = conn.getresponse() self.assertTrue(mock_describe_table.called) json_response = response.read() response_payload = json.loads(json_response) self.assertEqual(expected_response, response_payload)
def test_create_table(self, mock_create_table): mock_create_table.return_value = models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema(attribute_type_map={ "ForumName": models.AttributeType('S'), "Subject": models.AttributeType('S'), "LastPostDateTime": models.AttributeType('S') }, key_attributes=["ForumName", "Subject"], index_def_map={ "LastPostIndex": models.IndexDefinition( "ForumName", "LastPostDateTime") }), models.TableMeta.TABLE_STATUS_ACTIVE, 123) conn = httplib.HTTPConnection('localhost:8080') body = """ { "attribute_definitions": [ { "attribute_name": "ForumName", "attribute_type": "S" }, { "attribute_name": "Subject", "attribute_type": "S" }, { "attribute_name": "LastPostDateTime", "attribute_type": "S" } ], "table_name": "Thread", "key_schema": [ { "attribute_name": "ForumName", "key_type": "HASH" }, { "attribute_name": "Subject", "key_type": "RANGE" } ], "local_secondary_indexes": [ { "index_name": "LastPostIndex", "key_schema": [ { "attribute_name": "ForumName", "key_type": "HASH" }, { "attribute_name": "LastPostDateTime", "key_type": "RANGE" } ], "projection": { "projection_type": "KEYS_ONLY" } } ] } """ expected_response = { 'table_description': { 'attribute_definitions': [{ 'attribute_name': 'Subject', 'attribute_type': 'S' }, { 'attribute_name': 'LastPostDateTime', 'attribute_type': 'S' }, { 'attribute_name': 'ForumName', 'attribute_type': 'S' }], 'creation_date_time': 123, 'item_count': 0, 'key_schema': [{ 'attribute_name': 'ForumName', 'key_type': 'HASH' }, { 'attribute_name': 'Subject', 'key_type': 'RANGE' }], 'local_secondary_indexes': [{ 'index_name': 'LastPostIndex', 'index_size_bytes': 0, 'item_count': 0, 'key_schema': [{ 'attribute_name': 'ForumName', 'key_type': 'HASH' }, { 'attribute_name': 'LastPostDateTime', 'key_type': 'RANGE' }], 'projection': { 'projection_type': 'ALL' } }], 'table_id': '00000000-0000-0000-0000-000000000000', 'table_name': 'Thread', 'table_size_bytes': 0, 'table_status': 'ACTIVE', 'links': [{ 'href': self.table_url, 'rel': 'self' }, { 'href': self.table_url, 'rel': 'bookmark' }] } } conn.request("POST", self.url, headers=self.headers, body=body) response = conn.getresponse() self.assertTrue(mock_create_table.called) json_response = response.read() response_payload = json.loads(json_response) self.assertEqual(expected_response, response_payload)
def test_create_table_duplicate(self): self.storage_mocker.StubOutWithMock(storage, 'create_table') storage.create_table(IgnoreArg(), IgnoreArg(), IgnoreArg()).AndReturn( models.TableMeta( models.TableSchema( { 'hash': models.ATTRIBUTE_TYPE_NUMBER, 'range': models.ATTRIBUTE_TYPE_STRING, 'indexed_field': models.ATTRIBUTE_TYPE_STRING }, ['hash', 'range'], {"index_name": models.IndexDefinition('indexed_field')}), models.TableMeta.TABLE_STATUS_ACTIVE)) storage.create_table(IgnoreArg(), IgnoreArg(), IgnoreArg()).AndRaise(TableAlreadyExistsException) self.storage_mocker.ReplayAll() Table.create("test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('range', data_type=schema_types.STRING) ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.RangeKey('indexed_field', data_type=schema_types.STRING) ]) ], connection=self.DYNAMODB_CON) try: Table.create("test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('range', data_type=schema_types.STRING) ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.RangeKey( 'indexed_field', data_type=schema_types.STRING) ]) ], connection=self.DYNAMODB_CON) self.fail() except JSONResponseError as e: self.assertEqual('ResourceInUseException', e.error_code) self.storage_mocker.VerifyAll() except Exception as e: self.fail()
def test_create_table_no_sec_indexes(self, mock_create_table): mock_create_table.return_value = models.TableMeta( models.TableSchema(attribute_type_map={ "ForumName": models.ATTRIBUTE_TYPE_STRING, "Subject": models.ATTRIBUTE_TYPE_STRING, "LastPostDateTime": models.ATTRIBUTE_TYPE_STRING }, key_attributes=["ForumName", "Subject"]), models.TableMeta.TABLE_STATUS_ACTIVE) conn = httplib.HTTPConnection('localhost:8080') body = """ { "attribute_definitions": [ { "attribute_name": "ForumName", "attribute_type": "S" }, { "attribute_name": "Subject", "attribute_type": "S" }, { "attribute_name": "LastPostDateTime", "attribute_type": "S" } ], "table_name": "Thread", "key_schema": [ { "attribute_name": "ForumName", "key_type": "HASH" }, { "attribute_name": "Subject", "key_type": "RANGE" } ] } """ expected_response = { 'table_description': { 'attribute_definitions': [{ 'attribute_name': 'Subject', 'attribute_type': 'S' }, { 'attribute_name': 'LastPostDateTime', 'attribute_type': 'S' }, { 'attribute_name': 'ForumName', 'attribute_type': 'S' }], 'creation_date_time': 0, 'item_count': 0, 'key_schema': [{ 'attribute_name': 'ForumName', 'key_type': 'HASH' }, { 'attribute_name': 'Subject', 'key_type': 'RANGE' }], 'table_name': 'Thread', 'table_size_bytes': 0, 'table_status': 'ACTIVE', 'links': [{ 'href': self.table_url, 'rel': 'self' }, { 'href': self.table_url, 'rel': 'bookmark' }] } } conn.request("POST", self.url, headers=self.headers, body=body) response = conn.getresponse() self.assertTrue(mock_create_table.called) json_response = response.read() response_payload = json.loads(json_response) self.assertEqual(expected_response, response_payload)
def test_create_table_duplicate(self): self.storage_mocker.StubOutWithMock(storage, 'create_table') storage.create_table( mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg() ).AndReturn( models.TableMeta( '00000000-0000-0000-0000-000000000000', models.TableSchema( { 'hash': models.AttributeType('N'), 'range': models.AttributeType('S'), 'indexed_field': models.AttributeType('S') }, ['hash', 'range'], { "index_name": models.IndexDefinition('hash', 'indexed_field') } ), models.TableMeta.TABLE_STATUS_ACTIVE, None ) ) storage.create_table( mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg() ).AndRaise(exception.TableAlreadyExistsException) self.storage_mocker.ReplayAll() ddb_table.Table.create( "test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('range', data_type=schema_types.STRING) ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('indexed_field', data_type=schema_types.STRING) ] ) ], connection=self.DYNAMODB_CON ) try: ddb_table.Table.create( "test", schema=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('range', data_type=schema_types.STRING) ], throughput={ 'read': 20, 'write': 10, }, indexes=[ fields.KeysOnlyIndex( 'index_name', parts=[ fields.HashKey('hash', data_type=schema_types.NUMBER), fields.RangeKey('indexed_field', data_type=schema_types.STRING) ] ) ], connection=self.DYNAMODB_CON ) self.fail() except boto_exc.JSONResponseError as e: self.assertEqual('ResourceInUseException', e.error_code) self.assertEqual('Table already exists: test', e.body['message']) self.storage_mocker.VerifyAll() except Exception as e: self.fail()