Cleaning up
This commit is contained in:
23
spring-boot-starters/pom.xml
Normal file
23
spring-boot-starters/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
|
||||
<artifactId>spring-boot-starters</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>spring-boot-starters</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.kbastani</groupId>
|
||||
<artifactId>event-stream-processing-microservices</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>spring-boot-starter-aws-lambda</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1,92 @@
|
||||
# Spring Boot Starter AWS Lambda
|
||||
|
||||
This starter project provides auto-configuration support classes to easily invoke _AWS Lambda Functions_ from a Spring Boot application.
|
||||
|
||||
* Configuration property classes for externalizing AWS credentials as IAM access tokens
|
||||
* Automatic authenticated session management for securely invoking Lambda functions
|
||||
* Provides a Spring Boot friendly config adapter for easily registering Lambda function invocation interfaces
|
||||
|
||||
## Usage
|
||||
|
||||
In your Spring Boot project, add the starter project dependency to your class path. For Maven, add the following dependency to your `pom.xml`.
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.kbastani</groupId>
|
||||
<artifactId>spring-boot-starter-aws-lambda</artifactId>
|
||||
<version>${spring-boot-starter-aws-lambda.version}</version>
|
||||
</dependency>
|
||||
|
||||
...
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
Next, inject your AWS IAM credentials safely into the `application.properties|yaml` file for your Spring Boot application. The snippet below shows an example of how to source the access credentials from the environment.
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
profiles: development
|
||||
server:
|
||||
port: 8081
|
||||
amazon:
|
||||
aws:
|
||||
access-key-id: ${AWS_ACCESS_KEY_ID}
|
||||
access-key-secret: ${AWS_ACCESS_KEY_SECRET}
|
||||
```
|
||||
|
||||
You can also set the properties using command line arguments with the Maven Spring Boot plugin, shown in the snippet below.
|
||||
|
||||
```bash
|
||||
$ mvn spring-boot:run -Drun.arguments="--amazon.aws.access-key-id=ABCDEFG,--amazon.aws.access-key-secret=ZYXKGFWG"
|
||||
```
|
||||
|
||||
You can now begin to invoke AWS Lambda functions from your AWS account. The next thing you'll need to do is to define an interface of lambda function references to invoke.
|
||||
|
||||
```java
|
||||
public interface LambdaFunctions {
|
||||
|
||||
@LambdaFunction(functionName="account-created-13P0EDGLDE399", logType = LogType.Tail)
|
||||
Account accountCreated(AccountEvent event);
|
||||
|
||||
@LambdaFunction(functionName="account-activated-1P0I6FTFCMHKH", logType = LogType.Tail)
|
||||
Account accountActivated(AccountEvent event);
|
||||
}
|
||||
```
|
||||
|
||||
To start invoking your AWS Lambda functions, you can define a new bean that creates a proxy instance of your lambda interface.
|
||||
|
||||
```
|
||||
@Configuration
|
||||
public class AwsLambdaConfig {
|
||||
|
||||
@Bean
|
||||
public LambdaFunctions lambdaFunctions(AWSLambdaConfigurerAdapter configurerAdapter) {
|
||||
return configurerAdapter.getFunctionInstance(LambdaFunctions.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, we inject the auto-configured `AWSLambdaConfigurerAdapter` dependency from `spring-boot-starter-aws-lambda` into a new Spring bean definition named `lambdaFunctions`. The configurer adapter will create an instance of an interface that contains `@LambdaFunction` annotated methods—like in the example snippet for the `LambdaFunctions` interface.
|
||||
|
||||
We can now inject the `LambdaFunctions` as a dependency into other Spring components in our application in order to easily invoke remote Lambda functions on AWS.
|
||||
|
||||
```
|
||||
@Service
|
||||
public class AccountService {
|
||||
|
||||
final private LambdaFunctions lambdaFunctions;
|
||||
|
||||
public AccountService(LambdaFunctions lambdaFunctions) {
|
||||
this.lambdaFunctions = lambdaFunctions;
|
||||
}
|
||||
|
||||
public Account createAccount(Account account) {
|
||||
// Trigger the new event by invoking AWS lambda
|
||||
Account result = lambdaFunctions
|
||||
.accountCreated(new AccountEvent(account, EventType.ACCOUNT_CREATED));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
59
spring-boot-starters/spring-boot-starter-aws-lambda/pom.xml
Executable file
59
spring-boot-starters/spring-boot-starter-aws-lambda/pom.xml
Executable file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<artifactId>spring-boot-starter-aws-lambda</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.kbastani</groupId>
|
||||
<artifactId>spring-boot-starters</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-lambda</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-sts</artifactId>
|
||||
<version>${aws-java-sdk-sts.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-bom</artifactId>
|
||||
<version>${aws-java-sdk-sts.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,95 @@
|
||||
package amazon.aws;
|
||||
|
||||
import com.amazonaws.auth.*;
|
||||
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
|
||||
import com.amazonaws.services.lambda.invoke.LambdaFunction;
|
||||
import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
|
||||
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
|
||||
import com.amazonaws.services.securitytoken.model.Credentials;
|
||||
import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Provides a configurer for invoking remote AWS Lambda functions using {@link LambdaInvokerFactory}.
|
||||
* This component also manages the authenticated session for an IAM user that has provided valid
|
||||
* access keys to access AWS resources.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
@Component
|
||||
public class AWSLambdaConfigurerAdapter {
|
||||
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private Credentials sessionCredentials;
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link AWSLambdaConfigurerAdapter} with the bucket name and access credentials
|
||||
*
|
||||
* @param accessKeyId is the access key id credential for the specified bucket name
|
||||
* @param accessKeySecret is the access key secret for the specified bucket name
|
||||
*/
|
||||
public AWSLambdaConfigurerAdapter(String accessKeyId,
|
||||
String accessKeySecret) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proxy instance of a supplied interface that contains methods annotated with
|
||||
* {@link LambdaFunction}. Provides automatic credential support to authenticate with an IAM
|
||||
* access keys using {@link BasicSessionCredentials} auto-configured from Spring Boot
|
||||
* configuration properties in {@link AmazonProperties}.
|
||||
*
|
||||
* @param type
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> T getFunctionInstance(Class<T> type) {
|
||||
return LambdaInvokerFactory.builder()
|
||||
.lambdaClient(AWSLambdaClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(
|
||||
getBasicSessionCredentials()))
|
||||
.build())
|
||||
.build(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the basic session credentials for the template's configured IAM authentication keys
|
||||
*
|
||||
* @return a {@link BasicSessionCredentials} instance with a valid authenticated session token
|
||||
*/
|
||||
private BasicSessionCredentials getBasicSessionCredentials() {
|
||||
|
||||
// Create a new session token if the session is expired or not initialized
|
||||
if (sessionCredentials == null || sessionCredentials.getExpiration().before(new Date()))
|
||||
sessionCredentials = getSessionCredentials();
|
||||
|
||||
// Create basic session credentials using the generated session token
|
||||
return new BasicSessionCredentials(sessionCredentials.getAccessKeyId(),
|
||||
sessionCredentials.getSecretAccessKey(),
|
||||
sessionCredentials.getSessionToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new session credential that is valid for 12 hours
|
||||
*
|
||||
* @return an authenticated {@link Credentials} for the new session token
|
||||
*/
|
||||
private Credentials getSessionCredentials() {
|
||||
// Create a new session with the user credentials for the service instance
|
||||
AWSSecurityTokenServiceClient stsClient =
|
||||
new AWSSecurityTokenServiceClient(new BasicAWSCredentials(accessKeyId, accessKeySecret));
|
||||
|
||||
// Start a new session for managing a service instance's bucket
|
||||
GetSessionTokenRequest getSessionTokenRequest =
|
||||
new GetSessionTokenRequest().withDurationSeconds(43200);
|
||||
|
||||
// Get the session token for the service instance's bucket
|
||||
sessionCredentials = stsClient.getSessionToken(getSessionTokenRequest).getCredentials();
|
||||
|
||||
return sessionCredentials;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package amazon.aws;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* This class auto-configures a {@link AWSLambdaConfigurerAdapter} bean.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(AWSLambdaConfigurerAdapter.class)
|
||||
@EnableConfigurationProperties(AmazonProperties.class)
|
||||
public class AmazonAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private AmazonProperties amazonProperties;
|
||||
|
||||
@Bean
|
||||
protected AWSLambdaConfigurerAdapter lambdaAdapter() {
|
||||
return new AWSLambdaConfigurerAdapter(
|
||||
amazonProperties.getAws().getAccessKeyId(),
|
||||
amazonProperties.getAws().getAccessKeySecret());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package amazon.aws;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Provides a configuration properties model for authenticating with AWS.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "amazon")
|
||||
public class AmazonProperties {
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private Aws aws;
|
||||
|
||||
/**
|
||||
* A property group for Amazon Web Service (AWS) configurations
|
||||
*
|
||||
* @return a property group for AWS configurations
|
||||
*/
|
||||
public Aws getAws() {
|
||||
return aws;
|
||||
}
|
||||
|
||||
/**
|
||||
* A property group for Amazon Web Service (AWS) configurations
|
||||
*
|
||||
* @param aws is a property group for AWS configurations
|
||||
*/
|
||||
public void setAws(Aws aws) {
|
||||
this.aws = aws;
|
||||
}
|
||||
|
||||
/**
|
||||
* A property group for Amazon Web Service (AWS) configurations
|
||||
*/
|
||||
public static class Aws {
|
||||
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* A valid AWS account's access key id.
|
||||
*
|
||||
* @return an AWS access key id
|
||||
*/
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid AWS account's access key id.
|
||||
*
|
||||
* @param accessKeyId is a valid AWS account's access key id.
|
||||
*/
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid AWS account's secret access token.
|
||||
*
|
||||
* @return an AWS account's secret access key
|
||||
*/
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid AWS account's secret access token.
|
||||
*
|
||||
* @param accessKeySecret is a valid AWS account's secret access token.
|
||||
*/
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Aws{" +
|
||||
"accessKeyId='" + accessKeyId + '\'' +
|
||||
", accessKeySecret='" + accessKeySecret + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "amazon",
|
||||
"type": "amazon.aws.AmazonProperties",
|
||||
"sourceType": "amazon.aws.AmazonProperties"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=amazon.aws.AmazonAutoConfiguration
|
||||
@@ -0,0 +1,2 @@
|
||||
amazon.aws.access-key-id=replace
|
||||
amazon.aws.access-key-secret=replace
|
||||
@@ -0,0 +1,44 @@
|
||||
package amazon.aws;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
|
||||
public class AmazonConfigurationTest {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultAdapter() {
|
||||
load(EmptyConfiguration.class,
|
||||
"amazon.aws.access-key-id=AJGLDLSXKDFLS",
|
||||
"amazon.aws.access-key-secret=XSDFSDFLKKHASDFJALASDF");
|
||||
|
||||
AWSLambdaConfigurerAdapter amazonS3Template = this.context.getBean(AWSLambdaConfigurerAdapter.class);
|
||||
assertNotNull(amazonS3Template);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... environment) {
|
||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
|
||||
applicationContext.register(config);
|
||||
applicationContext.register(AmazonAutoConfiguration.class);
|
||||
applicationContext.refresh();
|
||||
this.context = applicationContext;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user