From 99cd3fe2269cebb4d93339a54822d264de14b6d4 Mon Sep 17 00:00:00 2001 From: Jim O'Mulloy Date: Mon, 13 Mar 2023 16:14:14 +0000 Subject: [PATCH] aws stuff --- instrument-aws/LICENSE | 21 +++ instrument-aws/README.md | 154 ++++++++++++++- instrument-aws/buildAndDeploy.sh | 6 + instrument-aws/buildAndDeployDontAsk.sh | 6 + instrument-aws/buildspec.yml | 20 ++ instrument-aws/cdk/README.md | 31 +++ instrument-aws/cdk/buildAndDeploy.sh | 3 + instrument-aws/cdk/buildAndDeployDontAsk.sh | 3 + instrument-aws/cdk/cdk.json | 16 ++ instrument-aws/cdk/destroy.sh | 3 + instrument-aws/cdk/pom.xml | 72 +++++++ .../cdk/src/main/java/airhacks/CDKApp.java | 45 +++++ .../airhacks/alb/boundary/LambdaAlbStack.java | 47 +++++ .../main/java/airhacks/alb/control/Alb.java | 24 +++ .../java/airhacks/alb/control/PublicVPC.java | 25 +++ .../boundary/LambdaApiGatewayStack.java | 24 +++ .../control/APIGatewayIntegrations.java | 46 +++++ .../boundary/FunctionURLStack.java | 24 +++ .../lambda/control/QuarkusLambda.java | 79 ++++++++ .../META-INF/quarkus-preload-classes.txt | 0 .../alb/boundary/LambdaAlbStackTest.java | 29 +++ .../boundary/LambdaApiGatewayStackTest.java | 29 +++ .../boundary/FunctionURLStackTest.java | 30 +++ instrument-aws/destroy.sh | 2 + instrument-aws/lambda-st/.dockerignore | 5 + instrument-aws/lambda-st/.gitignore | 39 ++++ instrument-aws/lambda-st/README.md | 5 + instrument-aws/lambda-st/pom.xml | 123 ++++++++++++ .../src/main/resources/application.properties | 2 + .../greetings/boundary/GreetingsResource.java | 18 ++ .../boundary/GreetingsResourceIT.java | 31 +++ instrument-aws/lambda/.dockerignore | 5 + instrument-aws/lambda/.gitignore | 39 ++++ instrument-aws/lambda/README.md | 60 ++++++ instrument-aws/lambda/pom.xml | 135 ++++++++++++++ .../lambda/greetings/boundary/Greeter.java | 27 +++ .../greetings/boundary/GreetingResource.java | 30 +++ .../resources/META-INF/resources/index.html | 176 ++++++++++++++++++ .../src/main/resources/application.properties | 1 + instrument-aws/s3uploader/README.md | 24 +++ instrument-aws/s3uploader/build/build.js | 41 ++++ .../s3uploader/build/check-versions.js | 54 ++++++ instrument-aws/s3uploader/build/utils.js | 101 ++++++++++ .../s3uploader/build/vue-loader.conf.js | 22 +++ .../s3uploader/build/webpack.base.conf.js | 92 +++++++++ .../s3uploader/build/webpack.dev.conf.js | 95 ++++++++++ .../s3uploader/build/webpack.prod.conf.js | 145 +++++++++++++++ instrument-aws/s3uploader/config/dev.env.js | 7 + instrument-aws/s3uploader/config/index.js | 76 ++++++++ instrument-aws/s3uploader/config/prod.env.js | 4 + instrument-aws/s3uploader/index.html | 12 ++ instrument-aws/s3uploader/package.json | 73 ++++++++ instrument-aws/s3uploader/src/App.vue | 31 +++ .../s3uploader/src/components/s3uploader.vue | 120 ++++++++++++ .../s3uploader/src/lambda/S3uploader.js | 44 +++++ instrument-aws/s3uploader/src/main.js | 13 ++ instrument-aws/s3uploader/static/.gitkeep | 0 instrument-aws/stspec.yml | 15 ++ instrument-server/payload.json | 4 + instrument-server/pom.xml | 121 ++++++++++++ .../src/main/java/jomu/InputObject.java | 25 +++ .../src/main/java/jomu/OutputObject.java | 26 +++ .../src/main/java/jomu/ProcessingService.java | 19 ++ .../src/main/java/jomu/StreamLambda.java | 24 +++ .../src/main/java/jomu/TestLambda.java | 19 ++ .../src/main/java/jomu/UnusedLambda.java | 19 ++ .../src/main/resources/application.properties | 1 + .../src/test/java/jomu/LambdaHandlerIT.java | 7 + .../src/test/java/jomu/LambdaHandlerTest.java | 32 ++++ .../src/test/resources/application.properties | 2 + pom.xml | 3 +- 71 files changed, 2704 insertions(+), 2 deletions(-) create mode 100644 instrument-aws/LICENSE create mode 100644 instrument-aws/buildAndDeploy.sh create mode 100644 instrument-aws/buildAndDeployDontAsk.sh create mode 100644 instrument-aws/buildspec.yml create mode 100644 instrument-aws/cdk/README.md create mode 100644 instrument-aws/cdk/buildAndDeploy.sh create mode 100644 instrument-aws/cdk/buildAndDeployDontAsk.sh create mode 100644 instrument-aws/cdk/cdk.json create mode 100644 instrument-aws/cdk/destroy.sh create mode 100644 instrument-aws/cdk/pom.xml create mode 100644 instrument-aws/cdk/src/main/java/airhacks/CDKApp.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/alb/boundary/LambdaAlbStack.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/alb/control/Alb.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/alb/control/PublicVPC.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/apigateway/boundary/LambdaApiGatewayStack.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/apigateway/control/APIGatewayIntegrations.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/functionurl/boundary/FunctionURLStack.java create mode 100644 instrument-aws/cdk/src/main/java/airhacks/lambda/control/QuarkusLambda.java create mode 100644 instrument-aws/cdk/src/main/resources/META-INF/quarkus-preload-classes.txt create mode 100644 instrument-aws/cdk/src/test/java/airhacks/alb/boundary/LambdaAlbStackTest.java create mode 100644 instrument-aws/cdk/src/test/java/airhacks/apigateway/boundary/LambdaApiGatewayStackTest.java create mode 100644 instrument-aws/cdk/src/test/java/airhacks/functionurl/boundary/FunctionURLStackTest.java create mode 100644 instrument-aws/destroy.sh create mode 100644 instrument-aws/lambda-st/.dockerignore create mode 100644 instrument-aws/lambda-st/.gitignore create mode 100644 instrument-aws/lambda-st/README.md create mode 100644 instrument-aws/lambda-st/pom.xml create mode 100644 instrument-aws/lambda-st/src/main/resources/application.properties create mode 100644 instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResource.java create mode 100644 instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResourceIT.java create mode 100644 instrument-aws/lambda/.dockerignore create mode 100644 instrument-aws/lambda/.gitignore create mode 100644 instrument-aws/lambda/README.md create mode 100644 instrument-aws/lambda/pom.xml create mode 100644 instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/Greeter.java create mode 100644 instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/GreetingResource.java create mode 100644 instrument-aws/lambda/src/main/resources/META-INF/resources/index.html create mode 100644 instrument-aws/lambda/src/main/resources/application.properties create mode 100644 instrument-aws/s3uploader/README.md create mode 100644 instrument-aws/s3uploader/build/build.js create mode 100644 instrument-aws/s3uploader/build/check-versions.js create mode 100644 instrument-aws/s3uploader/build/utils.js create mode 100644 instrument-aws/s3uploader/build/vue-loader.conf.js create mode 100644 instrument-aws/s3uploader/build/webpack.base.conf.js create mode 100644 instrument-aws/s3uploader/build/webpack.dev.conf.js create mode 100644 instrument-aws/s3uploader/build/webpack.prod.conf.js create mode 100644 instrument-aws/s3uploader/config/dev.env.js create mode 100644 instrument-aws/s3uploader/config/index.js create mode 100644 instrument-aws/s3uploader/config/prod.env.js create mode 100644 instrument-aws/s3uploader/index.html create mode 100644 instrument-aws/s3uploader/package.json create mode 100644 instrument-aws/s3uploader/src/App.vue create mode 100644 instrument-aws/s3uploader/src/components/s3uploader.vue create mode 100644 instrument-aws/s3uploader/src/lambda/S3uploader.js create mode 100644 instrument-aws/s3uploader/src/main.js create mode 100644 instrument-aws/s3uploader/static/.gitkeep create mode 100644 instrument-aws/stspec.yml create mode 100644 instrument-server/payload.json create mode 100644 instrument-server/pom.xml create mode 100644 instrument-server/src/main/java/jomu/InputObject.java create mode 100644 instrument-server/src/main/java/jomu/OutputObject.java create mode 100644 instrument-server/src/main/java/jomu/ProcessingService.java create mode 100644 instrument-server/src/main/java/jomu/StreamLambda.java create mode 100644 instrument-server/src/main/java/jomu/TestLambda.java create mode 100644 instrument-server/src/main/java/jomu/UnusedLambda.java create mode 100644 instrument-server/src/main/resources/application.properties create mode 100644 instrument-server/src/test/java/jomu/LambdaHandlerIT.java create mode 100644 instrument-server/src/test/java/jomu/LambdaHandlerTest.java create mode 100644 instrument-server/src/test/resources/application.properties diff --git a/instrument-aws/LICENSE b/instrument-aws/LICENSE new file mode 100644 index 00000000..8dc97d5e --- /dev/null +++ b/instrument-aws/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Adam Bien + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/instrument-aws/README.md b/instrument-aws/README.md index 337c32ee..9b5e1ef2 100644 --- a/instrument-aws/README.md +++ b/instrument-aws/README.md @@ -1,2 +1,154 @@ +# MicroProfile with Quarkus as AWS Lambda Function deployed with Cloud Development Kit (CDK) v2 for Java -![The Instrument Block Diagram](https://github.com/jimomulloy/instrument/blob/main/images/instrumentblocks.drawio.png) +A lean starting point for building, testing and deploying Quarkus MicroProfile applications deployed as AWS Lambda behind API Gateway. +The business logic, as well as, the Infrastructure as Code deployment are implemented with Java. + +# TL;DR + +A Quarkus MicroProfile application: + +```java + +@Path("hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + Greeter greeter; + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return this.greeter.greetings(); + } + + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void hello(String message) { + this.greeter.greetings(message); + } +} +``` +...with an additional dependency / [extension](https://quarkus.io/guides/amazon-lambda-http) for AWS REST APIs Gateway: + +```xml + + io.quarkus + quarkus-amazon-lambda-rest + +``` + +or HTTP APIs Gateway (default configuration): + +```xml + + io.quarkus + quarkus-amazon-lambda-http + +``` + +...deployed with AWS Cloud Development Kit: + +```java + +Function createFunction(String functionName,String functionHandler, + Map configuration, int memory, int maximumConcurrentExecution, int timeout) { + + return Function.Builder.create(this, functionName) + .runtime(Runtime.JAVA_11) + .code(Code.fromAsset("../lambda/target/function.zip")) + .handler(functionHandler) + .memorySize(memory) + .functionName(functionName) + .environment(configuration) + .timeout(Duration.seconds(timeout)) + .reservedConcurrentExecutions(maximumConcurrentExecution) + .build(); + } +``` +You choose between HTTP APIs gateway and REST APIs gateway with the `httpAPIGatewayIntegration` variable: + +``` java +public class CDKApp { + public static void main(final String[] args) { + + var app = new App(); + var appName = "quarkus-apigateway-lambda-cdk"; + Tags.of(app).add("project", "MicroProfile with Quarkus on AWS Lambda"); + Tags.of(app).add("environment","development"); + Tags.of(app).add("application", appName); + + var httpAPIGatewayIntegration = true; + new CDKStack(app, appName, true); + app.synth(); + } + } +} +``` + +## Prerequisites + +## Java + +1. Java / openJDK is installed +2. [Maven](https://maven.apache.org/) is installed + +## AWS + +Same installation as [aws-cdk-plain](https://github.com/AdamBien/aws-cdk-plain): + +0. For max convenience use the [`default` profile](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html). A profile named `default` doesn't have to be specificed with the `--profile` flag or configured in CDK applications. +1. Install [AWS CDK CLI](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) +2. [`cdk boostrap --profile YOUR_AWS_PROFILE`](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html) + +This template ships with AWS HTTP APIs Gateway. REST APIs Gateway is also supported. You can switch between both by using the corresponding extension (see [Choosing between HTTP APIs and REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html). + +Private APIs are only supported by [REST API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-apis.html). + +You can also build AWS Lambda `function.zip` and executable Quarkus JAR by extracting the extension into a Maven profile. Checkout: [https://adambien.blog/roller/abien/entry/hybrid_microprofile_deployments_with_quarkus](https://adambien.blog/roller/abien/entry/hybrid_microprofile_deployments_with_quarkus). + +See you at: [airhacks.live](https://airhacks.live) + +# in action + +## full build + +Build the Quarkus project `lambda` and deploy it with `cdk` as AWS Lambda: + +``` +cd lambda +./buildAndDeployDontAsk.sh +``` + +## continuous and accelerated deployment + +To continuously deploy the AWS Lambda at any changes, perform: + +``` +cd cdk +cdk watch +``` + +Now on every: `mvn package` in `lambda` directory / project the JAX-RS application is re-deployed automatically. + +## local deployment + +You can run the `lambda` project as regular Quarkus application with: + +`mvn compile quarkus:dev` + +The application is available under: `http://localhost:8080/hello` + +## Deploying MicroProfile / Quarkus Application as AWS Lambda with Java AWS CDK + +[![Deploying MicroProfile / Quarkus Application as AWS Lambda with Java AWS CDK](https://i.ytimg.com/vi/NA0WjIgp4CQ/mqdefault.jpg)](https://www.youtube.com/embed/NA0WjIgp4CQ?rel=0) + + +## Accelarating deployments with CDK v2 Watch + + +Using `cdk watch` for faster deployments + +[![Accelerating Deployment with CDK v2 Watch](https://i.ytimg.com/vi/SK7ic9wTYqU/mqdefault.jpg)](https://www.youtube.com/embed/SK7ic9wTYqU?rel=0) + +See you at: [airhacks.live](https://airhacks.live) diff --git a/instrument-aws/buildAndDeploy.sh b/instrument-aws/buildAndDeploy.sh new file mode 100644 index 00000000..85a02b1c --- /dev/null +++ b/instrument-aws/buildAndDeploy.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e +echo "building functions" +cd lambda && mvn clean package +echo "building CDK" +cd ../cdk && mvn clean package && cdk deploy \ No newline at end of file diff --git a/instrument-aws/buildAndDeployDontAsk.sh b/instrument-aws/buildAndDeployDontAsk.sh new file mode 100644 index 00000000..acd94236 --- /dev/null +++ b/instrument-aws/buildAndDeployDontAsk.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e +echo "building functions" +cd lambda && mvn clean package +echo "building CDK" +cd ../cdk && mvn clean package && cdk deploy --all --require-approval=never \ No newline at end of file diff --git a/instrument-aws/buildspec.yml b/instrument-aws/buildspec.yml new file mode 100644 index 00000000..a7c82bc3 --- /dev/null +++ b/instrument-aws/buildspec.yml @@ -0,0 +1,20 @@ +#https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax +version: 0.2 +phases: + install: + commands: + - echo "build started by ${CODEBUILD_INITIATOR}" + - echo "running on:" + - java -version + - mvn -version + - npm --version + - npm install -g aws-cdk + - cdk --version + build: + commands: + - echo "building AWS Lambda" + - cd ${CODEBUILD_SRC_DIR}/lambda && mvn --no-transfer-progress -DskipTests package + - echo "building cdk" + - cd ${CODEBUILD_SRC_DIR}/cdk && mvn --no-transfer-progress -DskipTests package + - echo "deploying application" + - cd ${CODEBUILD_SRC_DIR}/cdk && cdk deploy --all --require-approval=never diff --git a/instrument-aws/cdk/README.md b/instrument-aws/cdk/README.md new file mode 100644 index 00000000..8da46149 --- /dev/null +++ b/instrument-aws/cdk/README.md @@ -0,0 +1,31 @@ +# Slightly Streamlined AWS Cloud Development Kit (CDK) Boilerplate + +This is a blank, slightly streamlined, project for Java development with CDK. + +You will find the maven command in `cdk.json` file. + +It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests. + +## Installation + +1. Install [AWS CDK CLI](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) +2. [`cdk boostrap --profile YOUR_AWS_PROFILE`](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html) + +## Useful commands + + * `mvn package` compile and run tests + * `cdk ls` list all stacks in the app + * `cdk synth` emits the synthesized CloudFormation template + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk docs` open CDK documentation + +Enjoy! + +## in action + +[![Infrastructure as Java Code (IaJC): Setting AWS System Manager Parameter](https://i.ytimg.com/vi/eTG7EV1ThqQ/mqdefault.jpg)](https://www.youtube.com/embed/eTG7EV1ThqQ?rel=0) + + + +See you at: [airhacks.live](https://airhacks.live) \ No newline at end of file diff --git a/instrument-aws/cdk/buildAndDeploy.sh b/instrument-aws/cdk/buildAndDeploy.sh new file mode 100644 index 00000000..0bf7de1e --- /dev/null +++ b/instrument-aws/cdk/buildAndDeploy.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +mvn clean package && cdk deploy \ No newline at end of file diff --git a/instrument-aws/cdk/buildAndDeployDontAsk.sh b/instrument-aws/cdk/buildAndDeployDontAsk.sh new file mode 100644 index 00000000..4ee48db4 --- /dev/null +++ b/instrument-aws/cdk/buildAndDeployDontAsk.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +mvn clean package && cdk deploy --all --require-approval=never \ No newline at end of file diff --git a/instrument-aws/cdk/cdk.json b/instrument-aws/cdk/cdk.json new file mode 100644 index 00000000..1ec882e8 --- /dev/null +++ b/instrument-aws/cdk/cdk.json @@ -0,0 +1,16 @@ +{ + "app": "mvn -e -q compile exec:java", + "watch":{ + "include":[ + "../lambda/target/function.zip", + "../lambda/src/main/resources/application.properties" + ] + }, + "context": { + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true + } +} \ No newline at end of file diff --git a/instrument-aws/cdk/destroy.sh b/instrument-aws/cdk/destroy.sh new file mode 100644 index 00000000..8f70dd1b --- /dev/null +++ b/instrument-aws/cdk/destroy.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +cdk destroy \ No newline at end of file diff --git a/instrument-aws/cdk/pom.xml b/instrument-aws/cdk/pom.xml new file mode 100644 index 00000000..f35d2571 --- /dev/null +++ b/instrument-aws/cdk/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + airhacks + cdk + 0.1 + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + airhacks.CDKApp + + + + + + + + + software.amazon.awscdk + aws-cdk-lib + 2.66.1 + + + + software.amazon.awscdk + apigatewayv2-alpha + 2.66.1-alpha.0 + + + software.amazon.awscdk + apigatewayv2-integrations-alpha + 2.66.1-alpha.0 + + + software.constructs + constructs + 3.4.257 + + + org.junit.jupiter + junit-jupiter-api + 5.9.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + + UTF-8 + 17 + 17 + 17 + + \ No newline at end of file diff --git a/instrument-aws/cdk/src/main/java/airhacks/CDKApp.java b/instrument-aws/cdk/src/main/java/airhacks/CDKApp.java new file mode 100644 index 00000000..7666c77f --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/CDKApp.java @@ -0,0 +1,45 @@ +package airhacks; + +import airhacks.alb.boundary.LambdaAlbStack; +import airhacks.apigateway.boundary.LambdaApiGatewayStack; +import airhacks.functionurl.boundary.FunctionURLStack; +import software.amazon.awscdk.App; +import software.amazon.awscdk.Environment; +import software.amazon.awscdk.StackProps; +import software.amazon.awscdk.Tags; + + + +public class CDKApp { + + static StackProps createStackProperties() { + var account = System.getenv("CDK_DEPLOY_ACCOUNT"); + var region = System.getenv("CDK_DEPLOY_REGION"); + + if(account == null) + return StackProps.builder().build(); + + var environment = Environment.builder() + .account(account) + .region(region) + .build(); + return StackProps.builder().env(environment).build(); + } + + public static void main(final String[] args) { + + var app = new App(); + var appName = "quarkus-lambda"; + + Tags.of(app).add("project", "MicroProfile with Quarkus on AWS Lambda"); + Tags.of(app).add("environment","development"); + Tags.of(app).add("application", appName); + + var stackProps = createStackProperties(); + var snapStart = false; + new FunctionURLStack(app,appName,snapStart); + //new LambdaApiGatewayStack(app, appName); + //new LambdaAlbStack(app,appName); + app.synth(); + } +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/alb/boundary/LambdaAlbStack.java b/instrument-aws/cdk/src/main/java/airhacks/alb/boundary/LambdaAlbStack.java new file mode 100644 index 00000000..75bc690c --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/alb/boundary/LambdaAlbStack.java @@ -0,0 +1,47 @@ +package airhacks.alb.boundary; + +import java.util.List; + +import airhacks.alb.control.Alb; +import airhacks.alb.control.PublicVPC; +import airhacks.lambda.control.QuarkusLambda; +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.services.elasticloadbalancingv2.AddApplicationTargetsProps; +import software.amazon.awscdk.services.elasticloadbalancingv2.BaseApplicationListenerProps; +import software.amazon.awscdk.services.elasticloadbalancingv2.HealthCheck; +import software.amazon.awscdk.services.elasticloadbalancingv2.targets.LambdaTarget; +import software.constructs.Construct; + +public class LambdaAlbStack extends Stack { + + static String FUNCTION_NAME = "airhacks_lambda_gretings_boundary_Greetings"; + + public LambdaAlbStack(Construct scope, String id) { + super(scope, id+"-alb-stack"); + + var quarkuLambda = new QuarkusLambda(this,FUNCTION_NAME); + var publicVPCConstruct = new PublicVPC(this); + var publicVPC = publicVPCConstruct.getVpc(); + var alb = new Alb(this, publicVPC, "AirhacksLambdaLB"); + + var function = quarkuLambda.getFunction(); + var lambdaTarget = new LambdaTarget(function); + + var loadBalancer = alb.getApplicationLoadBalancer(); + var listener = loadBalancer.addListener("Http", BaseApplicationListenerProps.builder() + .port(80) + .build()); + + listener.addTargets("Lambda", AddApplicationTargetsProps.builder() + .targets(List.of(lambdaTarget)) + .healthCheck(HealthCheck.builder() + .enabled(true) + .build()) + .build()); + CfnOutput.Builder.create(this, "FunctionARN").value(function.getFunctionArn()).build(); + var url = loadBalancer.getLoadBalancerDnsName(); + CfnOutput.Builder.create(this, "LoadBalancerDNSName").value(url).build(); + CfnOutput.Builder.create(this, "LoadBalancerCurlOutput").value("curl -i http://"+url+"/hello").build(); + } +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/alb/control/Alb.java b/instrument-aws/cdk/src/main/java/airhacks/alb/control/Alb.java new file mode 100644 index 00000000..08fbe953 --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/alb/control/Alb.java @@ -0,0 +1,24 @@ +package airhacks.alb.control; + +import software.amazon.awscdk.services.ec2.Vpc; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationLoadBalancer; +import software.constructs.Construct; + +public class Alb extends Construct { + + ApplicationLoadBalancer applicationLoadBalancer; + + public Alb(Construct scope, Vpc vpc, String loadBalancerName) { + super(scope, "ApplicationLoadBalancer"); + + this.applicationLoadBalancer = ApplicationLoadBalancer.Builder.create(this, "ELB") + .internetFacing(true) + .loadBalancerName(loadBalancerName) + .vpc(vpc) + .build(); + } + + public ApplicationLoadBalancer getApplicationLoadBalancer(){ + return this.applicationLoadBalancer; + } +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/alb/control/PublicVPC.java b/instrument-aws/cdk/src/main/java/airhacks/alb/control/PublicVPC.java new file mode 100644 index 00000000..b04f073d --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/alb/control/PublicVPC.java @@ -0,0 +1,25 @@ +package airhacks.alb.control; + +import software.amazon.awscdk.services.ec2.Vpc; +import software.constructs.Construct; + +public class PublicVPC extends Construct { + + Vpc vpc; + + public PublicVPC(Construct scope) { + super(scope, "PublicVPC"); + this.vpc = Vpc.Builder.create(this, "VPC") + .cidr("10.0.0.0/16") + .enableDnsHostnames(true) + .enableDnsSupport(true) + .natGateways(0) + .maxAzs(2) + .build(); + } + + public Vpc getVpc(){ + return this.vpc; + } + +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/apigateway/boundary/LambdaApiGatewayStack.java b/instrument-aws/cdk/src/main/java/airhacks/apigateway/boundary/LambdaApiGatewayStack.java new file mode 100644 index 00000000..2b7df420 --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/apigateway/boundary/LambdaApiGatewayStack.java @@ -0,0 +1,24 @@ +package airhacks.apigateway.boundary; + +import airhacks.apigateway.control.APIGatewayIntegrations; +import airhacks.lambda.control.QuarkusLambda; +import software.amazon.awscdk.Stack; +import software.constructs.Construct; + +public class LambdaApiGatewayStack extends Stack { + + //true -> integrates with Http API, false -> integrates with REST API + static boolean HTTP_API_GATEWAY_INTEGRATION = true; + static String FUNCTION_NAME = "airhacks_lambda_gretings_boundary_Greetings"; + + public LambdaApiGatewayStack(Construct scope, String id) { + super(scope, id+"-apigateway-stack"); + var quarkuLambda = new QuarkusLambda(this,FUNCTION_NAME); + new APIGatewayIntegrations(this, HTTP_API_GATEWAY_INTEGRATION, quarkuLambda.getFunction()); + } + + + + + +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/apigateway/control/APIGatewayIntegrations.java b/instrument-aws/cdk/src/main/java/airhacks/apigateway/control/APIGatewayIntegrations.java new file mode 100644 index 00000000..4e3d6ebb --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/apigateway/control/APIGatewayIntegrations.java @@ -0,0 +1,46 @@ +package airhacks.apigateway.control; + +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.services.apigateway.LambdaRestApi; +import software.amazon.awscdk.services.apigatewayv2.alpha.HttpApi; +import software.amazon.awscdk.services.apigatewayv2.integrations.alpha.HttpLambdaIntegration; +import software.amazon.awscdk.services.lambda.IFunction; +import software.constructs.Construct; + +public class APIGatewayIntegrations extends Construct { + + public APIGatewayIntegrations(Construct scope, boolean httpAPIGatewayIntegration, IFunction function) { + super(scope, "APIGatewayIntegration"); + + if (httpAPIGatewayIntegration) + integrateWithHTTPApiGateway(function); + else + integrateWithRestApiGateway(function); + + CfnOutput.Builder.create(this, "FunctionHttpApiIntegration").value(String.valueOf(httpAPIGatewayIntegration)) + .build(); + CfnOutput.Builder.create(this, "FunctionArnOutput").value(function.getFunctionArn()).build(); + } + + /** + * https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html + */ + void integrateWithRestApiGateway(IFunction function) { + var apiGateway = LambdaRestApi.Builder.create(this, "RestApiGateway").handler(function).build(); + CfnOutput.Builder.create(this, "RestApiGatewayUrlOutput").value(apiGateway.getUrl()).build(); + + } + + /** + * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html + */ + void integrateWithHTTPApiGateway(IFunction function) { + var lambdaIntegration = HttpLambdaIntegration.Builder.create("HttpApiGatewayIntegration", function).build(); + var httpApiGateway = HttpApi.Builder.create(this, "HttpApiGatewayIntegration") + .defaultIntegration(lambdaIntegration).build(); + var url = httpApiGateway.getUrl(); + CfnOutput.Builder.create(this, "HttpApiGatewayUrlOutput").value(url).build(); + CfnOutput.Builder.create(this, "HttpApiGatewayCurlOutput").value("curl -i " + url + "hello").build(); + } + +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/functionurl/boundary/FunctionURLStack.java b/instrument-aws/cdk/src/main/java/airhacks/functionurl/boundary/FunctionURLStack.java new file mode 100644 index 00000000..a7aed12a --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/functionurl/boundary/FunctionURLStack.java @@ -0,0 +1,24 @@ +package airhacks.functionurl.boundary; + +import airhacks.lambda.control.QuarkusLambda; +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.services.lambda.FunctionUrlAuthType; +import software.amazon.awscdk.services.lambda.FunctionUrlOptions; +import software.constructs.Construct; + +public class FunctionURLStack extends Stack { + + static String FUNCTION_NAME = "airhacks_lambda_gretings_boundary_Greetings"; + + public FunctionURLStack(Construct construct,String id,boolean snapStart) { + super(construct,id+ "-function-url-stack"); + var quarkusLambda = new QuarkusLambda(this, FUNCTION_NAME,snapStart); + var function = quarkusLambda.getFunction(); + var functionUrl = function.addFunctionUrl(FunctionUrlOptions.builder() + .authType(FunctionUrlAuthType.NONE) + .build()); + CfnOutput.Builder.create(this, "FunctionURLOutput").value(functionUrl.getUrl()).build(); + + } +} diff --git a/instrument-aws/cdk/src/main/java/airhacks/lambda/control/QuarkusLambda.java b/instrument-aws/cdk/src/main/java/airhacks/lambda/control/QuarkusLambda.java new file mode 100644 index 00000000..fdbefd20 --- /dev/null +++ b/instrument-aws/cdk/src/main/java/airhacks/lambda/control/QuarkusLambda.java @@ -0,0 +1,79 @@ +package airhacks.lambda.control; + +import java.util.Map; + +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.services.lambda.Alias; +import software.amazon.awscdk.services.lambda.Architecture; +import software.amazon.awscdk.services.lambda.CfnFunction; +import software.amazon.awscdk.services.lambda.Code; +import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.IFunction; +import software.amazon.awscdk.services.lambda.Runtime; +import software.amazon.awscdk.services.lambda.Version; +import software.constructs.Construct; + +public class QuarkusLambda extends Construct { + + static Map configuration = Map.of( + "message", "hello, quarkus as AWS Lambda", + "JAVA_TOOL_OPTIONS", "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"); + static String lambdaHandler = "io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"; + static int memory = 1024; // ~0.5 vCPU + static int timeout = 10; + IFunction function; + + public QuarkusLambda(Construct scope, String functionName){ + this(scope,functionName,true); + } + + public QuarkusLambda(Construct scope, String functionName, boolean snapStart) { + super(scope, "QuarkusLambda"); + this.function = createFunction(functionName, lambdaHandler, configuration, memory, timeout,snapStart); + if (snapStart){ + var version = setupSnapStart(this.function); + this.function = createAlias(version); + } + } + + Version setupSnapStart(IFunction function) { + var defaultChild = this.function.getNode().getDefaultChild(); + if (defaultChild instanceof CfnFunction cfnFunction) { + cfnFunction.addPropertyOverride("SnapStart", Map.of("ApplyOn", "PublishedVersions")); + } + //a fresh logicalId enfoces code redeployment + var uniqueLogicalId = "SnapStartVersion"+System.currentTimeMillis(); + return Version.Builder.create(this, uniqueLogicalId) + .lambda(this.function) + .description("SnapStart") + .build(); + } + + + Alias createAlias(Version version){ + return Alias.Builder.create(this, "SnapstartAlias") + .aliasName("snapstart") + .description("this alias is required for SnapStart") + .version(version) + .build(); + } + + IFunction createFunction(String functionName, String functionHandler, Map configuration, int memory, + int timeout,boolean snapStart) { + var architecture = snapStart?Architecture.X86_64:Architecture.ARM_64; + return Function.Builder.create(this, functionName) + .runtime(Runtime.JAVA_11) + .architecture(architecture) + .code(Code.fromAsset("../lambda/target/function.zip")) + .handler(functionHandler) + .memorySize(memory) + .functionName(functionName) + .environment(configuration) + .timeout(Duration.seconds(timeout)) + .build(); + } + + public IFunction getFunction() { + return this.function; + } +} \ No newline at end of file diff --git a/instrument-aws/cdk/src/main/resources/META-INF/quarkus-preload-classes.txt b/instrument-aws/cdk/src/main/resources/META-INF/quarkus-preload-classes.txt new file mode 100644 index 00000000..e69de29b diff --git a/instrument-aws/cdk/src/test/java/airhacks/alb/boundary/LambdaAlbStackTest.java b/instrument-aws/cdk/src/test/java/airhacks/alb/boundary/LambdaAlbStackTest.java new file mode 100644 index 00000000..94e9802b --- /dev/null +++ b/instrument-aws/cdk/src/test/java/airhacks/alb/boundary/LambdaAlbStackTest.java @@ -0,0 +1,29 @@ +package airhacks.alb.boundary; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import org.junit.jupiter.api.Test; + +import software.amazon.awscdk.App; + +public class LambdaAlbStackTest { + private final static ObjectMapper JSON = + new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); + + @Test + public void testStack() throws IOException { + App app = new App(); + var stack = new LambdaAlbStack(app, "test"); + + // synthesize the stack to a CloudFormation template + var actual = JSON.valueToTree(app.synth().getStackArtifact(stack.getArtifactId()).getTemplate()); + + // Update once resources have been added to the stack + assertThat(actual.get("Resources")).isNotEmpty(); + } +} diff --git a/instrument-aws/cdk/src/test/java/airhacks/apigateway/boundary/LambdaApiGatewayStackTest.java b/instrument-aws/cdk/src/test/java/airhacks/apigateway/boundary/LambdaApiGatewayStackTest.java new file mode 100644 index 00000000..674ef065 --- /dev/null +++ b/instrument-aws/cdk/src/test/java/airhacks/apigateway/boundary/LambdaApiGatewayStackTest.java @@ -0,0 +1,29 @@ +package airhacks.apigateway.boundary; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import org.junit.jupiter.api.Test; + +import software.amazon.awscdk.App; + +public class LambdaApiGatewayStackTest { + private final static ObjectMapper JSON = + new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); + + @Test + public void testStack() throws IOException { + App app = new App(); + var stack = new LambdaApiGatewayStack(app, "test"); + + // synthesize the stack to a CloudFormation template + var actual = JSON.valueToTree(app.synth().getStackArtifact(stack.getArtifactId()).getTemplate()); + + // Update once resources have been added to the stack + assertThat(actual.get("Resources")).isNotEmpty(); + } +} diff --git a/instrument-aws/cdk/src/test/java/airhacks/functionurl/boundary/FunctionURLStackTest.java b/instrument-aws/cdk/src/test/java/airhacks/functionurl/boundary/FunctionURLStackTest.java new file mode 100644 index 00000000..a3b0c11c --- /dev/null +++ b/instrument-aws/cdk/src/test/java/airhacks/functionurl/boundary/FunctionURLStackTest.java @@ -0,0 +1,30 @@ +package airhacks.functionurl.boundary; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import org.junit.jupiter.api.Test; + +import software.amazon.awscdk.App; + +public class FunctionURLStackTest { + + private final static ObjectMapper JSON = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); + + @Test + public void functionURLSynth() throws IOException { + App app = new App(); + var stack = new FunctionURLStack(app, "function-url-stack",true); + + // synthesize the stack to a CloudFormation template + var actual = JSON.valueToTree(app.synth().getStackArtifact(stack.getArtifactId()).getTemplate()); + + // Update once resources have been added to the stack + assertThat(actual.get("Resources")).isNotEmpty(); + } + +} diff --git a/instrument-aws/destroy.sh b/instrument-aws/destroy.sh new file mode 100644 index 00000000..dd6028d0 --- /dev/null +++ b/instrument-aws/destroy.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd cdk && cdk destroy \ No newline at end of file diff --git a/instrument-aws/lambda-st/.dockerignore b/instrument-aws/lambda-st/.dockerignore new file mode 100644 index 00000000..94810d00 --- /dev/null +++ b/instrument-aws/lambda-st/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/instrument-aws/lambda-st/.gitignore b/instrument-aws/lambda-st/.gitignore new file mode 100644 index 00000000..bdf57ce3 --- /dev/null +++ b/instrument-aws/lambda-st/.gitignore @@ -0,0 +1,39 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env diff --git a/instrument-aws/lambda-st/README.md b/instrument-aws/lambda-st/README.md new file mode 100644 index 00000000..d0151d8f --- /dev/null +++ b/instrument-aws/lambda-st/README.md @@ -0,0 +1,5 @@ +# System Test / Black Box Tests + +To perform black box tests execute: + +mvn -Dbase_uri/mp-rest/url=https://[GENERATED_ID].execute-api.eu-central-1.amazonaws.com clean test-compile failsafe:integration-test \ No newline at end of file diff --git a/instrument-aws/lambda-st/pom.xml b/instrument-aws/lambda-st/pom.xml new file mode 100644 index 00000000..9b4915ac --- /dev/null +++ b/instrument-aws/lambda-st/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + airhacks + lambda-st + 0.0.1 + + 3.8.1 + false + 11 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.12.2.Final + 3.0.0-M5 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-rest-client + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + test + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/instrument-aws/lambda-st/src/main/resources/application.properties b/instrument-aws/lambda-st/src/main/resources/application.properties new file mode 100644 index 00000000..6d5d4ecd --- /dev/null +++ b/instrument-aws/lambda-st/src/main/resources/application.properties @@ -0,0 +1,2 @@ +base_uri/mp-rest/url=https://[REPLACE_WITH_GENERATED_ID].execute-api.eu-central-1.amazonaws.com/ +# ALB integration URIs come with the format: http://[LB-NAME-ID].eu-central-1.elb.amazonaws.com/ \ No newline at end of file diff --git a/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResource.java b/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResource.java new file mode 100644 index 00000000..c2940996 --- /dev/null +++ b/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResource.java @@ -0,0 +1,18 @@ +package airhacks.lambda.greetings.boundary; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient(configKey = "base_uri") +public interface GreetingsResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("hello") + String content(); + +} diff --git a/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResourceIT.java b/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResourceIT.java new file mode 100644 index 00000000..1b9d2c59 --- /dev/null +++ b/instrument-aws/lambda-st/src/test/java/airhacks/lambda/greetings/boundary/GreetingsResourceIT.java @@ -0,0 +1,31 @@ +package airhacks.lambda.greetings.boundary; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class GreetingsResourceIT { + + @Inject + @RestClient + GreetingsResource resource; + + @Inject + @ConfigProperty(name = "base_uri/mp-rest/url") + String baseURI; + + @Test + public void hello() { + var message = this.resource.content(); + assertNotNull(message); + System.out.println(message); + } + +} diff --git a/instrument-aws/lambda/.dockerignore b/instrument-aws/lambda/.dockerignore new file mode 100644 index 00000000..94810d00 --- /dev/null +++ b/instrument-aws/lambda/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/instrument-aws/lambda/.gitignore b/instrument-aws/lambda/.gitignore new file mode 100644 index 00000000..bdf57ce3 --- /dev/null +++ b/instrument-aws/lambda/.gitignore @@ -0,0 +1,39 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env diff --git a/instrument-aws/lambda/README.md b/instrument-aws/lambda/README.md new file mode 100644 index 00000000..fbcc5ac2 --- /dev/null +++ b/instrument-aws/lambda/README.md @@ -0,0 +1,60 @@ +# lambda Project + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +```shell script +./mvnw compile quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. + +## Packaging and running the application + +The application can be packaged using: +```shell script +./mvnw package +``` +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: +```shell script +./mvnw package -Dquarkus.package.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: +```shell script +./mvnw package -Pnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: +```shell script +./mvnw package -Pnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/lambda-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.html. + +## Related Guides + +- RESTEasy JAX-RS ([guide](https://quarkus.io/guides/rest-json)): REST endpoint framework implementing JAX-RS and more + +## Provided Code + +### RESTEasy JAX-RS + +Easily start your RESTful Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started#the-jax-rs-resources) diff --git a/instrument-aws/lambda/pom.xml b/instrument-aws/lambda/pom.xml new file mode 100644 index 00000000..d5baed4a --- /dev/null +++ b/instrument-aws/lambda/pom.xml @@ -0,0 +1,135 @@ + + + 4.0.0 + airhacks + lambda + 1.0.0-SNAPSHOT + + 3.8.1 + true + 11 + 11 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.16.3.Final + 3.0.0-M5 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + test + + + + io.quarkus + quarkus-amazon-lambda-http + + + + + + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + ${maven.compiler.parameters} + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/Greeter.java b/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/Greeter.java new file mode 100644 index 00000000..26f63261 --- /dev/null +++ b/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/Greeter.java @@ -0,0 +1,27 @@ +package airhacks.lambda.greetings.boundary; + +import static java.lang.System.Logger.Level.*; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@ApplicationScoped +public class Greeter { + + static System.Logger LOG = System.getLogger(Greeter.class.getName()); + + @Inject + @ConfigProperty(defaultValue = "hello, quarkus on AWS", name="message") + String message; + + public String greetings() { + LOG.log(INFO, "returning: " + this.message); + return this.message; + } + + public void greetings(String userMessage) { + LOG.log(INFO, "received: " + userMessage); + } +} diff --git a/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/GreetingResource.java b/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/GreetingResource.java new file mode 100644 index 00000000..0492d516 --- /dev/null +++ b/instrument-aws/lambda/src/main/java/airhacks/lambda/greetings/boundary/GreetingResource.java @@ -0,0 +1,30 @@ +package airhacks.lambda.greetings.boundary; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + Greeter greeter; + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return this.greeter.greetings(); + } + + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void hello(String message) { + this.greeter.greetings(message); + } +} \ No newline at end of file diff --git a/instrument-aws/lambda/src/main/resources/META-INF/resources/index.html b/instrument-aws/lambda/src/main/resources/META-INF/resources/index.html new file mode 100644 index 00000000..4fe5445c --- /dev/null +++ b/instrument-aws/lambda/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,176 @@ + + + + + lambda - 1.0.0-SNAPSHOT + + + + + + +
+
+

Congratulations, you have created a new Quarkus cloud application.

+ +

What is this page?

+ +

This page is served by Quarkus. The source is in + src/main/resources/META-INF/resources/index.html.

+ +

What are your next steps?

+ +

If not already done, run the application in dev mode using: ./mvnw compile quarkus:dev. +

+
    +
  • Your static assets are located in src/main/resources/META-INF/resources.
  • +
  • Configure your application in src/main/resources/application.properties.
  • +
  • Quarkus now ships with a Dev UI (available in dev mode only)
  • +
  • Play with the provided code located in src/main/java:
  • +
+
+

RESTEasy JAX-RS

+

Easily start your RESTful Web Services

+

@Path: /hello

+

Related guide section...

+
+ +
+
+
+

Application

+
    +
  • GroupId: airhacks
  • +
  • ArtifactId: lambda
  • +
  • Version: 1.0.0-SNAPSHOT
  • +
  • Quarkus Version: 2.4.2.Final
  • +
+
+
+

Do you like Quarkus?

+
    +
  • Go give it a star on GitHub.
  • +
+
+
+

Selected extensions guides

+ +
+ +
+
+ + \ No newline at end of file diff --git a/instrument-aws/lambda/src/main/resources/application.properties b/instrument-aws/lambda/src/main/resources/application.properties new file mode 100644 index 00000000..47ec8cb8 --- /dev/null +++ b/instrument-aws/lambda/src/main/resources/application.properties @@ -0,0 +1 @@ +%dev.quarkus.log.console.json=false \ No newline at end of file diff --git a/instrument-aws/s3uploader/README.md b/instrument-aws/s3uploader/README.md new file mode 100644 index 00000000..ee521145 --- /dev/null +++ b/instrument-aws/s3uploader/README.md @@ -0,0 +1,24 @@ +# s3uploader + +> A very simple Vue.JS project showing how to upload files from a web +> browser to an S3 bucket. + +> Contact @jbesw for questions, comments, issues, etc. + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/instrument-aws/s3uploader/build/build.js b/instrument-aws/s3uploader/build/build.js new file mode 100644 index 00000000..8f2ad8ad --- /dev/null +++ b/instrument-aws/s3uploader/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/instrument-aws/s3uploader/build/check-versions.js b/instrument-aws/s3uploader/build/check-versions.js new file mode 100644 index 00000000..3ef972a0 --- /dev/null +++ b/instrument-aws/s3uploader/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/instrument-aws/s3uploader/build/utils.js b/instrument-aws/s3uploader/build/utils.js new file mode 100644 index 00000000..e534fb0f --- /dev/null +++ b/instrument-aws/s3uploader/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/instrument-aws/s3uploader/build/vue-loader.conf.js b/instrument-aws/s3uploader/build/vue-loader.conf.js new file mode 100644 index 00000000..33ed58bc --- /dev/null +++ b/instrument-aws/s3uploader/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/instrument-aws/s3uploader/build/webpack.base.conf.js b/instrument-aws/s3uploader/build/webpack.base.conf.js new file mode 100644 index 00000000..1f4f47e4 --- /dev/null +++ b/instrument-aws/s3uploader/build/webpack.base.conf.js @@ -0,0 +1,92 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const createLintingRule = () => ({ + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter'), + emitWarning: !config.dev.showEslintErrorsInOverlay + } +}) + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + ...(config.dev.useEslint ? [createLintingRule()] : []), + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/instrument-aws/s3uploader/build/webpack.dev.conf.js b/instrument-aws/s3uploader/build/webpack.dev.conf.js new file mode 100644 index 00000000..070ae221 --- /dev/null +++ b/instrument-aws/s3uploader/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/instrument-aws/s3uploader/build/webpack.prod.conf.js b/instrument-aws/s3uploader/build/webpack.prod.conf.js new file mode 100644 index 00000000..d9f99f65 --- /dev/null +++ b/instrument-aws/s3uploader/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code splitted chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/instrument-aws/s3uploader/config/dev.env.js b/instrument-aws/s3uploader/config/dev.env.js new file mode 100644 index 00000000..1e22973a --- /dev/null +++ b/instrument-aws/s3uploader/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/instrument-aws/s3uploader/config/index.js b/instrument-aws/s3uploader/config/index.js new file mode 100644 index 00000000..926ab364 --- /dev/null +++ b/instrument-aws/s3uploader/config/index.js @@ -0,0 +1,76 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + // Use Eslint Loader? + // If true, your code will be linted during bundling and + // linting errors and warnings will be shown in the console. + useEslint: true, + // If true, eslint errors and warnings will also be shown in the error overlay + // in the browser. + showEslintErrorsInOverlay: false, + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/instrument-aws/s3uploader/config/prod.env.js b/instrument-aws/s3uploader/config/prod.env.js new file mode 100644 index 00000000..a6f99761 --- /dev/null +++ b/instrument-aws/s3uploader/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/instrument-aws/s3uploader/index.html b/instrument-aws/s3uploader/index.html new file mode 100644 index 00000000..3a9784eb --- /dev/null +++ b/instrument-aws/s3uploader/index.html @@ -0,0 +1,12 @@ + + + + + + s3uploader + + +
+ + + diff --git a/instrument-aws/s3uploader/package.json b/instrument-aws/s3uploader/package.json new file mode 100644 index 00000000..de82262c --- /dev/null +++ b/instrument-aws/s3uploader/package.json @@ -0,0 +1,73 @@ +{ + "name": "s3uploader", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "lint": "eslint --ext .js,.vue src", + "build": "node build/build.js" + }, + "dependencies": { + "axios": "^0.21.1", + "vue": "^2.6.12" + }, + "devDependencies": { + "autoprefixer": "^10.2.0", + "babel-core": "^6.26.3", + "babel-eslint": "^10.1.0", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^8.2.2", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-plugin-transform-vue-jsx": "^3.7.0", + "babel-preset-env": "^1.7.0", + "babel-preset-stage-2": "^6.24.1", + "chalk": "^4.1.0", + "copy-webpack-plugin": "^7.0.0", + "css-loader": "^5.0.1", + "eslint": "^7.17.0", + "eslint-config-standard": "^16.0.2", + "eslint-friendly-formatter": "^4.0.1", + "eslint-loader": "^1.7.1", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.1.0", + "eslint-plugin-vue": "^7.4.1", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^6.2.0", + "friendly-errors-webpack-plugin": "^1.7.0", + "html-webpack-plugin": "^4.5.1", + "node-notifier": "^9.0.0", + "optimize-css-assets-webpack-plugin": "^5.0.4", + "ora": "^5.2.0", + "portfinder": "^1.0.28", + "postcss-import": "^14.0.0", + "postcss-loader": "^4.1.0", + "postcss-url": "^10.1.1", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "shelljs": "^0.8.4", + "uglifyjs-webpack-plugin": "^2.2.0", + "url-loader": "^4.1.1", + "vue-loader": "^15.9.6", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.6.12", + "webpack": "^5.11.1", + "webpack-bundle-analyzer": "^4.3.0", + "webpack-dev-server": "^3.11.1", + "webpack-merge": "^5.7.3" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/instrument-aws/s3uploader/src/App.vue b/instrument-aws/s3uploader/src/App.vue new file mode 100644 index 00000000..ef1a241f --- /dev/null +++ b/instrument-aws/s3uploader/src/App.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/instrument-aws/s3uploader/src/components/s3uploader.vue b/instrument-aws/s3uploader/src/components/s3uploader.vue new file mode 100644 index 00000000..8ff228e3 --- /dev/null +++ b/instrument-aws/s3uploader/src/components/s3uploader.vue @@ -0,0 +1,120 @@ + + + + + + diff --git a/instrument-aws/s3uploader/src/lambda/S3uploader.js b/instrument-aws/s3uploader/src/lambda/S3uploader.js new file mode 100644 index 00000000..8572274e --- /dev/null +++ b/instrument-aws/s3uploader/src/lambda/S3uploader.js @@ -0,0 +1,44 @@ +// This is the function you will need in your Lambda to make the backend work. +// Follow along in the tutorial to see how to set this up. + +const uuidv4 = require('uuid/v4') +const AWS = require('aws-sdk') +AWS.config.update({ region: process.env.REGION || 'us-east-1' }) +const s3 = new AWS.S3(); + +const uploadBucket = '<< ENTER YOUR BUCKET NAME HERE >>' // << LOOK! + +exports.handler = async (event) => { + const result = await getUploadURL() + console.log('Result: ', result) + return result +}; + +const getUploadURL = async function() { + console.log('getUploadURL started') + let actionId = uuidv4() + + var s3Params = { + Bucket: uploadBucket, + Key: `${actionId}.jpg`, + ContentType: 'image/jpeg', + CacheControl: 'max-age=31104000', + ACL: 'public-read', + }; + + return new Promise((resolve, reject) => { + // Get signed URL + let uploadURL = s3.getSignedUrl('putObject', s3Params) + resolve({ + "statusCode": 200, + "isBase64Encoded": false, + "headers": { + "Access-Control-Allow-Origin": "*" + }, + "body": JSON.stringify({ + "uploadURL": uploadURL, + "photoFilename": `${actionId}.jpg` + }) + }) + }) +} \ No newline at end of file diff --git a/instrument-aws/s3uploader/src/main.js b/instrument-aws/s3uploader/src/main.js new file mode 100644 index 00000000..684ffacc --- /dev/null +++ b/instrument-aws/s3uploader/src/main.js @@ -0,0 +1,13 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' + +Vue.config.productionTip = false + +/* eslint-disable no-new */ +new Vue({ + el: '#app', + components: { App }, + template: '' +}) diff --git a/instrument-aws/s3uploader/static/.gitkeep b/instrument-aws/s3uploader/static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/instrument-aws/stspec.yml b/instrument-aws/stspec.yml new file mode 100644 index 00000000..05378ee4 --- /dev/null +++ b/instrument-aws/stspec.yml @@ -0,0 +1,15 @@ +#https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax +version: 0.2 +phases: + install: + commands: + - echo "build started by ${CODEBUILD_INITIATOR}" + - echo "running on:" + - java -version + - mvn -version + build: + commands: + - echo "compiling system test" + - cd ${CODEBUILD_SRC_DIR}/lambda-st && mvn --no-transfer-progress -DskipTests test-compile + - echo "performing system test" + - cd ${CODEBUILD_SRC_DIR}/lambda-st && mvn failsafe:integration-test failsafe:verify diff --git a/instrument-server/payload.json b/instrument-server/payload.json new file mode 100644 index 00000000..b81dea5a --- /dev/null +++ b/instrument-server/payload.json @@ -0,0 +1,4 @@ +{ + "name": "Bill", + "greeting": "hello" +} \ No newline at end of file diff --git a/instrument-server/pom.xml b/instrument-server/pom.xml new file mode 100644 index 00000000..185f91a0 --- /dev/null +++ b/instrument-server/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + + jomu + instrument-parent + 0.0.1-SNAPSHOT + + + instrument-server + + + 3.10.1 + true + 11 + 11 + UTF-8 + UTF-8 + 2.16.4.Final + quarkus-bom + io.quarkus + 2.16.4.Final + 3.0.0-M7 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-amazon-lambda + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-junit5 + test + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/instrument-server/src/main/java/jomu/InputObject.java b/instrument-server/src/main/java/jomu/InputObject.java new file mode 100644 index 00000000..cf453e5e --- /dev/null +++ b/instrument-server/src/main/java/jomu/InputObject.java @@ -0,0 +1,25 @@ +package jomu; + +public class InputObject { + + private String name; + private String greeting; + + public String getName() { + return name; + } + + public InputObject setName(String name) { + this.name = name; + return this; + } + + public String getGreeting() { + return greeting; + } + + public InputObject setGreeting(String greeting) { + this.greeting = greeting; + return this; + } +} diff --git a/instrument-server/src/main/java/jomu/OutputObject.java b/instrument-server/src/main/java/jomu/OutputObject.java new file mode 100644 index 00000000..315c27cd --- /dev/null +++ b/instrument-server/src/main/java/jomu/OutputObject.java @@ -0,0 +1,26 @@ +package jomu; + +public class OutputObject { + + private String result; + + private String requestId; + + public String getResult() { + return result; + } + + public String getRequestId() { + return requestId; + } + + public OutputObject setResult(String result) { + this.result = result; + return this; + } + + public OutputObject setRequestId(String requestId) { + this.requestId = requestId; + return this; + } +} diff --git a/instrument-server/src/main/java/jomu/ProcessingService.java b/instrument-server/src/main/java/jomu/ProcessingService.java new file mode 100644 index 00000000..d2bb7242 --- /dev/null +++ b/instrument-server/src/main/java/jomu/ProcessingService.java @@ -0,0 +1,19 @@ +package jomu; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class ProcessingService { + + public static final String CAN_ONLY_GREET_NICKNAMES = "Can only greet nicknames"; + + public OutputObject process(InputObject input) { + if (input.getName().equals("Stuart")) { + throw new IllegalArgumentException(CAN_ONLY_GREET_NICKNAMES); + } + String result = input.getGreeting() + " " + input.getName(); + OutputObject out = new OutputObject(); + out.setResult(result); + return out; + } +} diff --git a/instrument-server/src/main/java/jomu/StreamLambda.java b/instrument-server/src/main/java/jomu/StreamLambda.java new file mode 100644 index 00000000..edfc2899 --- /dev/null +++ b/instrument-server/src/main/java/jomu/StreamLambda.java @@ -0,0 +1,24 @@ +package jomu; + +import javax.inject.Named; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + + +@Named("stream") +public class StreamLambda implements RequestStreamHandler { + + @Override + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { + int letter; + while ((letter = inputStream.read()) != -1) { + int character = Character.toUpperCase(letter); + outputStream.write(character); + } + } +} diff --git a/instrument-server/src/main/java/jomu/TestLambda.java b/instrument-server/src/main/java/jomu/TestLambda.java new file mode 100644 index 00000000..25673a46 --- /dev/null +++ b/instrument-server/src/main/java/jomu/TestLambda.java @@ -0,0 +1,19 @@ +package jomu; + +import javax.inject.Inject; +import javax.inject.Named; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +@Named("test") +public class TestLambda implements RequestHandler { + + @Inject + ProcessingService service; + + @Override + public OutputObject handleRequest(InputObject input, Context context) { + return service.process(input).setRequestId(context.getAwsRequestId()); + } +} diff --git a/instrument-server/src/main/java/jomu/UnusedLambda.java b/instrument-server/src/main/java/jomu/UnusedLambda.java new file mode 100644 index 00000000..629e231b --- /dev/null +++ b/instrument-server/src/main/java/jomu/UnusedLambda.java @@ -0,0 +1,19 @@ +package jomu; + +import javax.inject.Inject; +import javax.inject.Named; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +@Named("unused") +public class UnusedLambda implements RequestHandler { + + @Inject + ProcessingService service; + + @Override + public OutputObject handleRequest(InputObject input, Context context) { + throw new RuntimeException("Should be unused"); + } +} diff --git a/instrument-server/src/main/resources/application.properties b/instrument-server/src/main/resources/application.properties new file mode 100644 index 00000000..560e17fd --- /dev/null +++ b/instrument-server/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.lambda.handler=test \ No newline at end of file diff --git a/instrument-server/src/test/java/jomu/LambdaHandlerIT.java b/instrument-server/src/test/java/jomu/LambdaHandlerIT.java new file mode 100644 index 00000000..886fa108 --- /dev/null +++ b/instrument-server/src/test/java/jomu/LambdaHandlerIT.java @@ -0,0 +1,7 @@ +package jomu; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class LambdaHandlerIT extends LambdaHandlerTest { +} diff --git a/instrument-server/src/test/java/jomu/LambdaHandlerTest.java b/instrument-server/src/test/java/jomu/LambdaHandlerTest.java new file mode 100644 index 00000000..cf328d2a --- /dev/null +++ b/instrument-server/src/test/java/jomu/LambdaHandlerTest.java @@ -0,0 +1,32 @@ +package jomu; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; + +@QuarkusTest +public class LambdaHandlerTest { + + @Test + public void testSimpleLambdaSuccess() throws Exception { + // you test your lambdas by invoking on http://localhost:8081 + // this works in dev mode too + + InputObject in = new InputObject(); + in.setName("Stu"); + in.setGreeting("Hello"); + given() + .contentType("application/json") + .accept("application/json") + .body(in) + .when() + .post() + .then() + .statusCode(200) + .body(containsString("Hello Stu")); + } + +} diff --git a/instrument-server/src/test/resources/application.properties b/instrument-server/src/test/resources/application.properties new file mode 100644 index 00000000..6be7ba73 --- /dev/null +++ b/instrument-server/src/test/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.lambda.handler=test + diff --git a/pom.xml b/pom.xml index 36a55a34..88019850 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,8 @@ instrument-core instrument-desktop instrument-command - + instrument-server + 3.8.1