javaamazon-web-servicesamazon-dynamodbamazon-dynamodb-local

Why do I get "Unable to load AWS credentials" when using DynamoDB locally in Java?


I am writing a functional test case and connecting to a local DyanmoDB instance within it.

This is how I create the DynamoDB client:

private static final String table_name = "table-A";
private static String aws_region = "us-west-2";
private static AmazonDynamoDB amazonDynamoDBClient;
private static DynamoDB dynamoDB;


public static void initDB() {
    amazonDynamoDBClient = AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(
      new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", aws_region)).build();
    dynamoDB = new DynamoDB(amazonDynamoDBClient);
}

And this is how I'm trying to create the table:

Table table = dynamoDB.createTable(table_name,
                Arrays.asList(new KeySchemaElement("row_name", KeyType.HASH),
                        new KeySchemaElement("row_id", KeyType.RANGE)),
                Arrays.asList(new AttributeDefinition("row_name", ScalarAttributeType.S),
                        new AttributeDefinition("row_id", ScalarAttributeType.S)),
                new ProvisionedThroughput(10L, 10L));

However, I get the below exception:

com.amazonaws.SdkClientException: Unable to load AWS credentials from any provider in the chain
        at com.amazonaws.auth.AWSCredentialsProviderChain.getCredentials(AWSCredentialsProviderChain.java:131)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.getCredentialsFromContext(AmazonHttpClient.java:1115)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.runBeforeRequestHandlers(AmazonHttpClient.java:764)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:728)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:721)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:704)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:672)
        at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:654)
        at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:518)
        at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.doInvoke(AmazonDynamoDBClient.java:1831)
        at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.invoke(AmazonDynamoDBClient.java:1807)
        at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.createTable(AmazonDynamoDBClient.java:636)
        at com.amazonaws.services.dynamodbv2.document.DynamoDB.createTable(DynamoDB.java:96)
        at com.amazonaws.services.dynamodbv2.document.DynamoDB.createTable(DynamoDB.java:108)
        at util.DBUtil.createAndInsertTestCases(DBUtil.java:24)
        at test.DocumentTest.createAndInsertDataInDB(DocumentTest.java:37)

What is the issue?


Solution

  • It looks like you never have set up any credentials for AWS on your machine for the SDK to use.


    The DynamoDB SDK is oblivious to if you are using a local or remote cloud version of DynamoDB. This means it will still be checking if credentials are present before sending the request.

    As most people will be using the remote cloud version of DDB, which requires credentials, this allows the client to not call the remote server when it knows the request will fail - faster error handling, no network cost, etc.

    You can see that as the stack traces show that AmazonHttpClient.java is trying to get credentials using getCredentialsFromContext.


    An access key ID & secret access key must be set for all requests but for the local DDB, these can be set to any dummy & fake values.

    This should work, for example:

    EndpointConfiguration endpointConfig = new EndpointConfiguration("http://localhost:8000", aws_region);
    
    AWSStaticCredentialsProvider awsCredProvider = new AWSStaticCredentialsProvider(new BasicAWSCredentials("fakeMyKeyId", "fakeSecretAccessKey"));
    
    AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder
        .standard()
        .withCredentials(awsCredProvider)
        .withEndpointConfiguration(endpointConfig)
        .build();
    

    As @jarmod also points out in the comments, if you cannot provide credentials for some reason, you may also be able to use AnonymousAWSCredentials (assuming that local DDB doesn't actually require signed API requests).

    Specifying the credentials provider as AnonymousCredentialsProvider may work:

    EndpointConfiguration endpointConfig = new EndpointConfiguration("http://localhost:8000", aws_region);
    
    AnonymousCredentialsProvider awsCredProvider = new AnonymousCredentialsProvider();
    
    AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder
        .standard()
        .withCredentials(awsCredProvider)
        .withEndpointConfiguration(endpointConfig)
        .build();