GH-530, GH-630 Improvements to AWS Custom Runtime

This commit provides initial set of improvements to executing functions in AWS Custom Runtime
- Consistent invocation model for functional as well as @Bean configuration models via new CustomRuntimeEventLoop as well as AWSLambdaUtils
- Clean up classpath to decrease the size of the JAR/ZIP file
- Configuration simplification which no longer requires enabling of function exporter

It also allows user to define functions that rely on AWS types such as APIGatewayProxyRequestEvent

The existing invocation model remains in tact for the time being. Both invocation models are mutually exclusing in theit setup to avoid potential conflict.

Resolves #538
Resolves #630
This commit is contained in:
Oleg Zhurakousky
2021-01-21 22:02:02 +01:00
parent 175c819ae9
commit a1d10f0771
13 changed files with 706 additions and 18 deletions

View File

@@ -0,0 +1,12 @@
This sample uses the custom runtime type on AWS lambda using @Bean style configuration.
However, changing configuration to functional bean registration is supported as well and shown in `function-sample-aws-custom` example.
To run the app in AWS choose the "Custom Runtime" runtime type, and upload the
.zip file that gets built on the command line with `mvn package` (look
in `target`).
There are several functions defined in the `com.example.LambdaApplication`, so identify the selected function in "Handler"
You can also use function composition (e.g., `uppercase|reverse`)
You can test any function in this example with any String as input, but the Lambda UI only allows valid JSON as
test data, so you will have to escape the input with double quotes.

View File

@@ -0,0 +1,210 @@
<?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 https://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>2.4.2-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>io.spring.sample</groupId>
<artifactId>function-sample-aws-custom-bean</artifactId>
<version>3.0.0.RELEASE</version>
<name>AWS Custom Runtime - @Bean sample</name>
<description>Demo project for Spring Cloud Function with custom AWS Lambda runtime using @Bean style</description>
<properties>
<java.version>1.8</java.version>
<wrapper.version>1.0.22.RELEASE</wrapper.version>
<spring-cloud-function.version>3.1.1-SNAPSHOT</spring-cloud-function.version>
</properties>
<dependencies>
<dependency>
<!--
We don't need this dependency unless explicitly using APIGatewayProxyRequestEvent
(as on of the function in this example)
-->
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.14.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-dependencies</artifactId>
<version>${spring-cloud-function.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>com/example/ContainerTests.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>${wrapper.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>zip</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<inherited>false</inherited>
</execution>
</executions>
<configuration>
<descriptors>
<descriptor>src/assembly/zip.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>com/example/ContainerTests.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<!-- <repositories> -->
<!-- <repository> -->
<!-- <id>spring-snapshots</id> -->
<!-- <name>Spring Snapshots</name> -->
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
<!-- <snapshots> -->
<!-- <enabled>true</enabled> -->
<!-- </snapshots> -->
<!-- <releases> -->
<!-- <enabled>false</enabled> -->
<!-- </releases> -->
<!-- </repository> -->
<!-- <repository> -->
<!-- <id>spring-milestones</id> -->
<!-- <name>Spring Milestones</name> -->
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
<!-- <snapshots> -->
<!-- <enabled>false</enabled> -->
<!-- </snapshots> -->
<!-- </repository> -->
<!-- <repository> -->
<!-- <id>spring-releases</id> -->
<!-- <name>Spring Releases</name> -->
<!-- <url>https://repo.spring.io/release</url> -->
<!-- <snapshots> -->
<!-- <enabled>false</enabled> -->
<!-- </snapshots> -->
<!-- </repository> -->
<!-- </repositories> -->
<!-- <pluginRepositories> -->
<!-- <pluginRepository> -->
<!-- <id>spring-snapshots</id> -->
<!-- <name>Spring Snapshots</name> -->
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
<!-- <snapshots> -->
<!-- <enabled>true</enabled> -->
<!-- </snapshots> -->
<!-- <releases> -->
<!-- <enabled>false</enabled> -->
<!-- </releases> -->
<!-- </pluginRepository> -->
<!-- <pluginRepository> -->
<!-- <id>spring-milestones</id> -->
<!-- <name>Spring Milestones</name> -->
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
<!-- <snapshots> -->
<!-- <enabled>false</enabled> -->
<!-- </snapshots> -->
<!-- </pluginRepository> -->
<!-- <pluginRepository> -->
<!-- <id>spring-releases</id> -->
<!-- <name>Spring Releases</name> -->
<!-- <url>https://repo.spring.io/libs-release-local</url> -->
<!-- <snapshots> -->
<!-- <enabled>false</enabled> -->
<!-- </snapshots> -->
<!-- </pluginRepository> -->
<!-- </pluginRepositories> -->
</project>

View File

@@ -0,0 +1,35 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 https://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>zip</id>
<formats>
<format>zip</format>
</formats>
<baseDirectory></baseDirectory>
<fileSets>
<fileSet>
<directory>target/classes</directory>
<outputDirectory>/</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<excludes>
<exclude>bootstrap</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>target/classes</directory>
<outputDirectory>/</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<fileMode>0775</fileMode>
<includes>
<include>bootstrap</include>
</includes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<unpack>false</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>

View File

@@ -0,0 +1,71 @@
package com.example;
import java.util.Arrays;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.util.ObjectUtils;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
@SpringBootApplication
public class LambdaApplication {
private static Log logger = LogFactory.getLog(LambdaApplication.class);
@Bean
public Function<String, String> uppercase() {
return value -> {
logger.info("UPPERCASING: " + value);
return value.toUpperCase();
};
}
@Bean
public Function<APIGatewayProxyRequestEvent, String> extractPayloadFromGatewayEvent() {
return value -> {
logger.info("ECHO Payload from Gateway Event: " + value.getBody());
return value.getBody();
};
}
@Bean
public Function<Message<String>, Message<String>> echoMessage() {
return value -> {
logger.info("ECHO MESSAGE: " + value);
return value;
};
}
@Bean
public Function<String, String> reverse() {
return value -> {
logger.info("REVERSING: " + value);
return new StringBuilder(value).reverse().toString();
};
}
public static void main(String[] args) {
System.out.println("=====> ENVIRONMENT: " + System.getenv("AWS_LAMBDA_RUNTIME_API"));
//FunctionalSpringApplication.run(LambdaApplication.class, args);
logger.info("==> Starting: LambdaApplication");
if (!ObjectUtils.isEmpty(args)) {
logger.info("==> args: " + Arrays.asList(args));
}
SpringApplication.run(LambdaApplication.class, args);
}
// @Override
// public void initialize(GenericApplicationContext context) {
// context.registerBean("uppercase", FunctionRegistration.class,
// () -> new FunctionRegistration<>(uppercase()).type(
// FunctionType.from(String.class).to(String.class)));
// }
}

View File

@@ -0,0 +1,2 @@
spring.main.web-application-type=none
logging.level.org.springframework.cloud=DEBUG

View File

@@ -0,0 +1,8 @@
#!/bin/sh
cd ${LAMBDA_TASK_ROOT:-.}
java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \
-noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \
-Djava.security.egd=file:/dev/./urandom \
-cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication

View File

@@ -0,0 +1,6 @@
#!/bin/sh
while true
do
sleep 1
done