Skip to content

Added example java Application with steps to connect to Amazon Keyspa… #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
27 changes: 27 additions & 0 deletions java/datastax-v4/connection-EKS-SigV4/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Start with a base image containing Java runtime
FROM --platform=linux/amd64 openjdk:17-jdk-alpine

# Make port 8080 available to the world outside this container
EXPOSE 8080

# Add a volume pointing to /tmp
VOLUME /tmp

# The application's jar file
ARG JAR_FILE=target/mykeyspacessigv4springbootapp-1.0-SNAPSHOT.jar

# Add the application's jar to the container
COPY ${JAR_FILE} app.jar

# Add the truststore to the container
COPY cassandra_truststore.jks /usr/app/cassandra_truststore.jks

# Add the application's properties to the container
COPY src/main/resources/application.conf /usr/app/application.conf

# Add the application's logback configuration to the container
COPY src/main/resources/logback.xml /usr/app/logback.xml

# Run the jar file
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

98 changes: 98 additions & 0 deletions java/datastax-v4/connection-EKS-SigV4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Connection to Amazon Keyspaces from Amazon EKS using SigV4 Authentication

This example demonstrates how to connect to Amazon Keyspaces from an Amazon EKS cluster using SigV4 authentication.

## Prerequisites

- Amazon EKS cluster
- Amazon Keyspaces
- AWS CLI
- kubectl
- Docker
- Amazon ECR

## Setup

### Clone the Repository

```shell
git clone https://github.com/naveenkantamneni/amazon-keyspaces-examples.git
```

## Create Keyspace and Table in Amazon Keyspaces

```
CREATE KEYSPACE aws WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'} AND durable_writes = true;
CREATE TABLE aws.user (
username text PRIMARY KEY,
fname text,
last_update_date timestamp,
lname text
);
```

## Create your Amazon EKS Cluster by following steps in below page
https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html

## Create an OIDC provider for your EKS cluster by following instructions on below page
https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html

## Create a Kubernetes Service Account to assume an IAM role by following the instructions on below page. Follow Instructions under AWS CLI section and make sure to add IAM Policy "AmazonKeyspacesFullAccess" to your IAM Role in step "g"
https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html

## Confirm that the "AmazonKeyspacesFullAccess" policy that you attached to your role in a previous step is attached to the role.

```
aws iam list-attached-role-policies --role-name myeks-servicerole-assumewebid
```

An example output is as follows

```
{
"AttachedPolicies": [
{
"PolicyName": "AmazonKeyspacesFullAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonKeyspacesFullAccess"
}
]
}
```

## Create your Amazon ECR repository
```
aws ecr create-repository --repository-name <my repository>
```

## Navigate to the base Directory, Modify the Dockerfile as needed and build the Docker Image. Make sure to use your AWS Account ID, AWS Region where ECR repository created, ECR repository name and your tag during build

```
cd amazon-keyspaces-examples/java/datastax-v4/connection-EKS-SigV4
docker build -t <aws_account_id>.dkr.ecr.<AWS_region>.amazonaws.com/<ECR_repositoryname>:<your_tag> .
```

## Retrieve Authentication Token to push the image to ECR
```
aws ecr get-login-password --region <AWS_region> | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<AWS_region>.amazonaws.com
```

## Push Docker Image to Amazon ECR repository
```
docker push <aws_account_id>.dkr.ecr.<AWS_region>.amazonaws.com/<ECR_repositoryname>:<your_tag>
```

## Modify deployment.yaml with relevant information and Deploy Application to Amazon EKS
```
kubectl apply -f deployment.yaml
```

## Check the POD status
```
kubectl get pods -n <your namespace>
```

### Optional, Validate Logs to ensure connectivity to Amazon Keyspaces
```
kubectl logs -f <POD_name> -n <your namespace>
```

Binary file not shown.
33 changes: 33 additions & 0 deletions java/datastax-v4/connection-EKS-SigV4/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mykeyspacessigv4springbootapp
namespace: <EKS namespace>
spec:
replicas: 1
selector:
matchLabels:
app: mykeyspacessigv4springbootapp
template:
metadata:
labels:
app: mykeyspacessigv4springbootapp
spec:
serviceAccountName: <eks-service-account>
containers:
- name: <ECR Repository name>
image: <aws_account_id>.dkr.ecr.<AWS_region>.amazonaws.com/<ECR_repositoryname>:<your_tag>
ports:
- containerPort: 8080
env:
- name: CASSANDRA_HOST
value: "<Keyspaces Endpoint>:9142"
- name: CASSANDRA_DC
value: "<keyspaces AWS region>"
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
- name: AWS_ROLE_ARN
value: "arn:aws:iam::<AWS Account ID>:role/<IAM Role>"
- name: AWS_REGION
value: "<AWS Region>"

76 changes: 76 additions & 0 deletions java/datastax-v4/connection-EKS-SigV4/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>mykeyspacessigv4springbootapp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.15.0</version>
</dependency>
<dependency>
<groupId>software.aws.mcs</groupId>
<artifactId>aws-sigv4-auth-cassandra-java-driver-plugin</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sts</artifactId>
<version>1.11.717</version> <!-- Match this version to your aws-java-sdk version -->
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.example.mykeyspacessigv4springbootapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

import javax.annotation.PreDestroy;
import java.io.File;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import javax.net.ssl.SSLContext;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.ConsistencyLevel;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import software.aws.mcs.auth.SigV4AuthProvider;

@SpringBootApplication
public class Mykeyspacessigv4springbootappApplication {

private CqlSession session;

public static void main(String[] args) {
SpringApplication.run(Mykeyspacessigv4springbootappApplication.class, args);
}

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new ClassPathResource("application.conf"));
properties.setIgnoreResourceNotFound(false);
properties.setIgnoreUnresolvablePlaceholders(false);
return properties;
}

@Bean
public CqlSession cqlSession() throws NoSuchAlgorithmException {
String cassandraHost = System.getenv("CASSANDRA_HOST");
String cassandraDC = System.getenv("CASSANDRA_DC");

var builder = CqlSession.builder()
.addContactPoint(new InetSocketAddress(cassandraHost.split(":")[0], Integer.parseInt(cassandraHost.split(":")[1])))
.withAuthProvider(new SigV4AuthProvider(cassandraDC))
.withLocalDatacenter(cassandraDC)
.withSslContext(SSLContext.getDefault())
.withConfigLoader(DriverConfigLoader.fromClasspath("application.conf"));

session = builder.build();

// Insert statement
String query = "INSERT INTO aws.user (username,fname,last_update_date,lname) VALUES ('test','random',dateOf(now()),'k')";
SimpleStatement statement = SimpleStatement.newInstance(query).setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
session.execute(statement);
System.out.println("Insert operation was successful. The following row was added:");
System.out.println("Username: test");
System.out.println("First Name: random");
System.out.println("Last Update Date: " + LocalDate.now());
System.out.println("Last Name: k");

return session;
}

@PreDestroy
public void preDestroy() {
if (session != null) {
session.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
datastax-java-driver {
basic.contact-points = ["cassandra.us-east-1.amazonaws.com:9142"]
basic.load-balancing-policy.local-datacenter = "us-east-1"
advanced.auth-provider {
class = software.aws.mcs.auth.SigV4AuthProvider
aws-region = "us-east-1"
}
advanced.ssl-engine-factory {
class = DefaultSslEngineFactory
truststore-path = "/usr/app/cassandra_truststore.jks"
truststore-password = "test"
}
}
cassandra {
host = ${?CASSANDRA_HOST}
dc = ${?CASSANDRA_DC}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.mykeyspacessigv4springbootapp;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Mykeyspacessigv4springbootappApplicationTests {

@Test
void contextLoads() {
}

}