Example #1
0
def test_update_ttl_errors(dynamodb):
    client = dynamodb.meta.client
    # Can't set TTL on a non-existent table
    nonexistent_table = unique_table_name()
    with pytest.raises(ClientError, match='ResourceNotFoundException'):
        client.update_time_to_live(TableName=nonexistent_table,
                                   TimeToLiveSpecification={
                                       'AttributeName': 'expiration',
                                       'Enabled': True
                                   })
    with new_test_table(dynamodb,
                        KeySchema=[
                            {
                                'AttributeName': 'p',
                                'KeyType': 'HASH'
                            },
                        ],
                        AttributeDefinitions=[{
                            'AttributeName': 'p',
                            'AttributeType': 'S'
                        }]) as table:
        # AttributeName must be between 1 and 255 characters long.
        with pytest.raises(ClientError, match='ValidationException.*length'):
            client.update_time_to_live(TableName=table.name,
                                       TimeToLiveSpecification={
                                           'AttributeName': 'x' * 256,
                                           'Enabled': True
                                       })
        with pytest.raises(ClientError, match='ValidationException.*length'):
            client.update_time_to_live(TableName=table.name,
                                       TimeToLiveSpecification={
                                           'AttributeName': '',
                                           'Enabled': True
                                       })
        # Missing mandatory UpdateTimeToLive parameters - AttributeName or Enabled
        with pytest.raises(ClientError,
                           match='ValidationException.*[aA]ttributeName'):
            client.update_time_to_live(
                TableName=table.name,
                TimeToLiveSpecification={'Enabled': True})
        with pytest.raises(ClientError,
                           match='ValidationException.*[eE]nabled'):
            client.update_time_to_live(
                TableName=table.name,
                TimeToLiveSpecification={'AttributeName': 'hello'})
        # Wrong types for these mandatory parameters (e.g., string for Enabled)
        # The error type is currently a bit different in Alternator
        # (ValidationException) and in DynamoDB (SerializationException).
        with pytest.raises(ClientError):
            client.update_time_to_live(TableName=table.name,
                                       TimeToLiveSpecification={
                                           'AttributeName': 'hello',
                                           'Enabled': 'dog'
                                       })
        with pytest.raises(ClientError):
            client.update_time_to_live(TableName=table.name,
                                       TimeToLiveSpecification={
                                           'AttributeName': 3,
                                           'Enabled': True
                                       })
Example #2
0
def test_forbidden_tags_from_creation(scylla_only, dynamodb):
    # The feature of creating a table already with tags was only added to
    # DynamoDB in April 2019, and to the botocore library in version 1.12.136
    # so older versions of the library cannot run this test.
    import botocore
    from distutils.version import LooseVersion
    if (LooseVersion(botocore.__version__) < LooseVersion('1.12.136')):
        pytest.skip(
            "Botocore version 1.12.136 or above required to run this test")
    name = unique_table_name()
    # It is not allowed to set the system:write_isolation to "dog", so the
    # following table creation should fail:
    with pytest.raises(ClientError, match='ValidationException'):
        dynamodb.create_table(TableName=name,
                              BillingMode='PAY_PER_REQUEST',
                              KeySchema=[{
                                  'AttributeName': 'p',
                                  'KeyType': 'HASH'
                              }],
                              AttributeDefinitions=[{
                                  'AttributeName': 'p',
                                  'AttributeType': 'S'
                              }],
                              Tags=[{
                                  'Key': 'system:write_isolation',
                                  'Value': 'dog'
                              }])
    # After the table creation failed, the table should not exist.
    with pytest.raises(ClientError, match='ResourceNotFoundException'):
        dynamodb.meta.client.describe_table(TableName=name)
Example #3
0
def test_create_table_billing_mode_errors(dynamodb, test_table):
    with pytest.raises(ClientError, match='ValidationException'):
        create_table(dynamodb, unique_table_name(), BillingMode='unknown')
    # billing mode is case-sensitive
    with pytest.raises(ClientError, match='ValidationException'):
        create_table(dynamodb,
                     unique_table_name(),
                     BillingMode='pay_per_request')
    # PAY_PER_REQUEST cannot come with a ProvisionedThroughput:
    with pytest.raises(ClientError, match='ValidationException'):
        create_table(dynamodb,
                     unique_table_name(),
                     BillingMode='PAY_PER_REQUEST',
                     ProvisionedThroughput={
                         'ReadCapacityUnits': 10,
                         'WriteCapacityUnits': 10
                     })
    # On the other hand, PROVISIONED requires ProvisionedThroughput:
    # By the way, ProvisionedThroughput not only needs to appear, it must
    # have both ReadCapacityUnits and WriteCapacityUnits - but we can't test
    # this with boto3, because boto3 has its own verification that if
    # ProvisionedThroughput is given, it must have the correct form.
    with pytest.raises(ClientError, match='ValidationException'):
        create_table(dynamodb, unique_table_name(), BillingMode='PROVISIONED')
    # If BillingMode is completely missing, it defaults to PROVISIONED, so
    # ProvisionedThroughput is required
    with pytest.raises(ClientError, match='ValidationException'):
        dynamodb.create_table(TableName=unique_table_name(),
                              KeySchema=[{
                                  'AttributeName': 'p',
                                  'KeyType': 'HASH'
                              }],
                              AttributeDefinitions=[{
                                  'AttributeName': 'p',
                                  'AttributeType': 'S'
                              }])
Example #4
0
def test_too_long_tags_from_creation(dynamodb):
    # The feature of creating a table already with tags was only added to
    # DynamoDB in April 2019, and to the botocore library in version 1.12.136
    # so older versions of the library cannot run this test.
    import botocore
    if (Version(botocore.__version__) < Version('1.12.136')):
        pytest.skip("Botocore version 1.12.136 or above required to run this test")
    name = unique_table_name()
    # Setting 100 tags is not allowed, the following table creation should fail:
    with pytest.raises(ClientError, match='ValidationException'):
        dynamodb.create_table(TableName=name,
            BillingMode='PAY_PER_REQUEST',
            KeySchema=[{ 'AttributeName': 'p', 'KeyType': 'HASH' }],
            AttributeDefinitions=[{ 'AttributeName': 'p', 'AttributeType': 'S' }],
            Tags=[{'Key': str(i), 'Value': str(i)} for i in range(100)])
    # After the table creation failed, the table should not exist.
    with pytest.raises(ClientError, match='ResourceNotFoundException'):
        dynamodb.meta.client.describe_table(TableName=name)
Example #5
0
def test_concurrent_create_and_delete_table(dynamodb, table_def,
                                            fails_without_raft):
    # According to boto3 documentation, "Unlike Resources and Sessions,
    # clients are generally thread-safe.". So because we have two threads
    # in this test, we must not use "dynamodb" (containing the boto3
    # "resource") - we should only the boto3 "client":
    client = dynamodb.meta.client

    # Unfortunately by default Python threads print their exceptions
    # (e.g., assertion failures) but don't propagate them to the join(),
    # so the overall test doesn't fail. The following Thread wrapper
    # causes join() to rethrow the exception, so the test will fail.
    class ThreadWrapper(threading.Thread):
        def run(self):
            try:
                self.ret = self._target(*self._args, **self._kwargs)
            except BaseException as e:
                self.exception = e

        def join(self, timeout=None):
            super().join(timeout)
            if hasattr(self, 'exception'):
                raise self.exception
            return self.ret

    table_name = unique_table_name()
    # The more iterations we do, the higher the chance of reproducing
    # this issue. On my laptop, count = 10 reproduces the bug every time.
    # Lower numbers have some chance of not catching the bug. If this
    # issue starts to xpass, we may need to increase the count.
    count = 10

    def deletes():
        for i in range(count):
            try:
                client.delete_table(TableName=table_name)
            except Exception as e:
                # Expect either success or a ResourceNotFoundException.
                # On DynamoDB we can also get a ResourceInUseException
                # if we try to delete a table while it's in the middle
                # of being created.
                # Anything else (e.g., InternalServerError) is a bug.
                assert isinstance(
                    e, ClientError) and ('ResourceNotFoundException' in str(e)
                                         or 'ResourceInUseException' in str(e))
            else:
                print("delete successful")

    def creates():
        for i in range(count):
            try:
                client.create_table(TableName=table_name,
                                    BillingMode='PAY_PER_REQUEST',
                                    **table_def)
            except Exception as e:
                # Expect either success or a ResourceInUseException.
                # Anything else (e.g., InternalServerError) is a bug.
                assert isinstance(
                    e, ClientError) and 'ResourceInUseException' in str(e)
            else:
                print("create successful")

    t1 = ThreadWrapper(target=deletes)
    t2 = ThreadWrapper(target=creates)
    t1.start()
    t2.start()
    try:
        t1.join()
        t2.join()
    finally:
        # Make sure that in any case, the table is deleted before the
        # test finishes. On DynamoDB, we can't just call DeleteTable -
        # if some CreateTable is still in progress we can't call
        # DeleteTable until it finishes...
        timeout = time.time() + 120
        while time.time() < timeout:
            try:
                client.delete_table(TableName=table_name)
                break
            except ClientError as e:
                if 'ResourceNotFoundException' in str(e):
                    # The table was already deleted by the deletion thread,
                    # nothing left to do :-)
                    break
                if 'ResourceInUseException' in str(e):
                    # A CreateTable opereration is still in progress,
                    # we can't delete the table yet.
                    time.sleep(1)
                    continue
                raise