diff --git a/README.md b/README.md index 482dd0489..c7bc3529c 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ $ cdk destroy | Example | Description | |---------|-------------| | [hello-world](https://github.com/aws-samples/aws-cdk-examples/tree/master/java/hello-world/) | A demo application that uses the CDK in Java | +| [lambda-cron](https://github.com/aws-samples/aws-cdk-examples/tree/master/java/lambda-cron/) | Running a Lambda on a schedule | ## Python examples diff --git a/java/lambda-cron/README.md b/java/lambda-cron/README.md new file mode 100644 index 000000000..a962a244c --- /dev/null +++ b/java/lambda-cron/README.md @@ -0,0 +1,32 @@ +# CDK Java Example + +This an example of a CDK program written in Java. + +## Building + +To build this app, run `mvn compile`. This will download the required +dependencies compile the Java code. + +You can use your IDE to write code and unit tests, but you will need to use the +CDK toolkit if you wish to synthesize/deploy stacks. + +## CDK Toolkit + +The [`cdk.json`](./cdk.json) file in the root of this repository includes +instructions for the CDK toolkit on how to execute this program. + +Specifically, it will tell the toolkit to use the `mvn exec:java` command as the +entry point of your application. After changing your Java code, you will be able +to run the CDK toolkits commands as usual (Maven will recompile as needed): + + $ cdk ls + + + $ cdk synth + + + $ cdk deploy + + + $ cdk diff + diff --git a/java/lambda-cron/cdk.json b/java/lambda-cron/cdk.json new file mode 100644 index 000000000..237b891bf --- /dev/null +++ b/java/lambda-cron/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "mvn exec:java -Dexec.mainClass=software.amazon.awscdk.examples.LambdaCronApp" +} diff --git a/java/lambda-cron/pom.xml b/java/lambda-cron/pom.xml new file mode 100644 index 000000000..6cdd5f2f6 --- /dev/null +++ b/java/lambda-cron/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + com.amazonaws.cdk + lambda-cron + 1.0.0 + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + jar-with-dependencies + + + + com.amazonaws.cdk.examples.LambdaCronApp + + + + + + make-assembly + package + + single + + + + + + + + + + + software.amazon.jsii + jsii-runtime + 0.7.14 + + + + software.amazon.awscdk + cdk + 0.23.0 + + + + software.amazon.awscdk + lambda + 0.23.0 + + + + + com.fasterxml.jackson.core + jackson-core + [2.9.8,) + + + + + com.fasterxml.jackson.core + jackson-databind + [2.9.8,) + + + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-core + + + + + org.hamcrest + hamcrest-library + 1.3 + test + + + + + diff --git a/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronApp.java b/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronApp.java new file mode 100644 index 000000000..25597cb7b --- /dev/null +++ b/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronApp.java @@ -0,0 +1,13 @@ +package software.amazon.awscdk.examples; + +import software.amazon.awscdk.App; + +public class LambdaCronApp { + public static void main(final String[] args) { + App app = new App(); + + new LambdaCronStack(app, "cdk-lambda-cron-example"); + + app.run(); + } +} diff --git a/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronStack.java b/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronStack.java new file mode 100644 index 000000000..398796232 --- /dev/null +++ b/java/lambda-cron/src/main/java/software/amazon/awscdk/examples/LambdaCronStack.java @@ -0,0 +1,44 @@ +package software.amazon.awscdk.examples; + +import software.amazon.awscdk.App; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.services.events.EventRule; +import software.amazon.awscdk.services.events.EventRuleProps; +import software.amazon.awscdk.services.lambda.Code; +import software.amazon.awscdk.services.lambda.Runtime; +import software.amazon.awscdk.services.lambda.SingletonFunction; +import software.amazon.awscdk.services.lambda.SingletonFunctionProps; + +import java.util.UUID; + +/** + * Lambda Cron CDK example for Java! + */ +class LambdaCronStack extends Stack { + public LambdaCronStack(final App parent, final String name) { + super(parent, name); + + SingletonFunction lambdaFunction = new SingletonFunction(this, "cdk-lambda-cron", + SingletonFunctionProps.builder() + .withFunctionName("CDK Lambda Cron Example") + .withDescription("Lambda which prints \"I'm running\"") + .withCode(Code.inline( + "def main(event, context):\n" + + " print(\"I'm running!\")\n")) + .withHandler("index.main") + .withTimeout(300) + .withRuntime(Runtime.PYTHON27) + .withUuid(UUID.randomUUID().toString()) + .build() + ); + + EventRule rule = new EventRule(this, "cdk-lambda-cron-rule", + EventRuleProps.builder() + .withDescription("Run every day at 6PM UTC") + .withScheduleExpression("cron(0 18 ? * MON-FRI *)") + .build() + ); + + rule.addTarget(lambdaFunction); + } +} diff --git a/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/LambdaCronStackTest.java b/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/LambdaCronStackTest.java new file mode 100644 index 000000000..78f8112c4 --- /dev/null +++ b/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/LambdaCronStackTest.java @@ -0,0 +1,81 @@ +package software.amazon.awscdk.examples; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Before; +import org.junit.Test; +import software.amazon.awscdk.App; +import software.amazon.awscdk.Stack; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.junit.Assert.assertEquals; +import static software.amazon.awscdk.examples.TestUtils.toCloudFormationJson; + +public class LambdaCronStackTest { + private App app; + private Stack stack; + private JsonNode actualStack; + private JsonNode expectedStack; + + @Before + public void setUp() throws IOException { + app = new App(); + stack = new LambdaCronStack(app, "lambdaResource-cdk-lambda-cron"); + actualStack = toCloudFormationJson(stack).path("Resources"); + expectedStack = TestUtils.fromFileResource(getClass().getResource("testCronLambdaExpected.json")).path("Resources"); + } + + @Test() + public void testTypes() { + List actual = actualStack.findValues("Type"); + List expected = expectedStack.findValues("Type"); + containsInAnyOrder(actual, expected); + } + + @Test + public void testPermission() { + final String type = "AWS::Lambda::Permission"; + JsonNode actual = TestUtils.getJsonNode(actualStack, type); + JsonNode expected = TestUtils.getJsonNode(expectedStack, type); + String[] keys = {"ManagedPolicyArns", "AssumeRolePolicyDocument"}; + for (String key : keys) { + assertEquals(actual.get(key), expected.get(key)); + } + } + + @Test + public void testRole() { + final String type = "AWS::IAM::Role"; + JsonNode actual = TestUtils.getJsonNode(actualStack, type); + JsonNode expected = TestUtils.getJsonNode(expectedStack, type); + String[] keys = {"ManagedPolicyArns", "AssumeRolePolicyDocument"}; + for (String key : keys) { + assertEquals(actual.get(key), expected.get(key)); + } + } + + @Test + public void testLambda() { + final String type = "AWS::Lambda::Function"; + JsonNode actual = TestUtils.getJsonNode(actualStack, type); + JsonNode expected = TestUtils.getJsonNode(expectedStack, type); + String[] keys = {"Runtime", "Description", "Timeout", "Handler", "Code"}; + for (String key : keys) { + assertEquals(actual.get(key), expected.get(key)); + } + } + + @Test + public void testRule() { + final String type = "AWS::Events::Rule"; + JsonNode actual = TestUtils.getJsonNode(actualStack, type); + JsonNode expected = TestUtils.getJsonNode(expectedStack, type); + String[] keys = {"ScheduleExpression", "Description", "State"}; + for (String key : keys) { + assertEquals(actual.get(key), expected.get(key)); + } + } + +} \ No newline at end of file diff --git a/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/TestUtils.java b/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/TestUtils.java new file mode 100644 index 000000000..65c944b3b --- /dev/null +++ b/java/lambda-cron/src/test/java/software/amazon/awscdk/examples/TestUtils.java @@ -0,0 +1,54 @@ +package software.amazon.awscdk.examples; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.awscdk.Stack; + +import java.io.IOException; +import java.net.URL; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import static org.junit.Assert.assertEquals; + +public class TestUtils { + private static ObjectMapper JSON = new ObjectMapper(); + + static JsonNode fromFileResource(final URL fileResource) throws IOException { + return JSON.readTree(fileResource); + } + + static JsonNode toCloudFormationJson(final Stack stack) throws IOException { + JsonNode n = JSON.valueToTree(stack.toCloudFormation()); + System.out.println(JSON.writerWithDefaultPrettyPrinter().writeValueAsString(n)); + return n; + } + + static void assertTemplate(final Stack stack, final URL expectedResource) throws IOException { + assertTemplate(stack, JSON.readTree(expectedResource)); + } + + static void assertTemplate(final Stack stack, final String expectedTemplate) throws IOException { + assertTemplate(stack, JSON.readTree(expectedTemplate)); + } + + private static void assertTemplate(final Stack stack, final JsonNode expected) throws IOException { + JsonNode actual = toCloudFormationJson(stack); + + // print to stderr if non-equal, so it will be easy to grab + if (expected == null || !expected.equals(actual)) { + String prettyActual = JSON.writerWithDefaultPrettyPrinter().writeValueAsString(actual); + System.err.println(prettyActual); + } + + assertEquals(expected, actual); + } + + static JsonNode getJsonNode(JsonNode stackJson, String type) { + Iterable iterable = stackJson::elements; + Optional maybe = StreamSupport.stream(iterable.spliterator(), false) + .filter(jn -> jn.get("Type").textValue().equals(type)).findFirst(); + return maybe.map(jn -> jn.path("Properties")).orElseThrow(NoSuchElementException::new); + } +} diff --git a/java/lambda-cron/src/test/resources/software/amazon/awscdk/examples/testCronLambdaExpected.json b/java/lambda-cron/src/test/resources/software/amazon/awscdk/examples/testCronLambdaExpected.json new file mode 100644 index 000000000..9359bbbd8 --- /dev/null +++ b/java/lambda-cron/src/test/resources/software/amazon/awscdk/examples/testCronLambdaExpected.json @@ -0,0 +1,67 @@ +{ + "Resources" : { + "cdklambdacronrule709B40EE" : { + "Type" : "AWS::Events::Rule", + "Properties" : { + "ScheduleExpression" : "cron(0 18 ? * MON-FRI *)", + "Description" : "Run every day at 6PM UTC", + "State" : "ENABLED", + "Targets" : [ { + "Id" : "cdk-lambda-cron", + "Arn" : { + "Fn::GetAtt" : [ "SingletonLambda0dc9f04c622c4059b399834a2c1ac15f9CB1DC29", "Arn" ] + } + } ] + } + }, + "SingletonLambda0dc9f04c622c4059b399834a2c1ac15fServiceRoleD59355BD" : { + "Type" : "AWS::IAM::Role", + "Properties" : { + "ManagedPolicyArns" : [ { + "Fn::Join" : [ "", [ "arn:", { + "Ref" : "AWS::Partition" + }, ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] ] + } ], + "AssumeRolePolicyDocument" : { + "Version" : "2012-10-17", + "Statement" : [ { + "Action" : "sts:AssumeRole", + "Effect" : "Allow", + "Principal" : { + "Service" : "lambda.amazonaws.com" + } + } ] + } + } + }, + "SingletonLambda0dc9f04c622c4059b399834a2c1ac15fAllowEventRulelambdaResourcecdklambdacroncdklambdacronruleD22C5DC3B70D87DC" : { + "Type" : "AWS::Lambda::Permission", + "Properties" : { + "FunctionName" : { + "Ref" : "SingletonLambda0dc9f04c622c4059b399834a2c1ac15f9CB1DC29" + }, + "Action" : "lambda:InvokeFunction", + "SourceArn" : { + "Fn::GetAtt" : [ "cdklambdacronrule709B40EE", "Arn" ] + }, + "Principal" : "events.amazonaws.com" + } + }, + "SingletonLambda0dc9f04c622c4059b399834a2c1ac15f9CB1DC29" : { + "Type" : "AWS::Lambda::Function", + "Properties" : { + "Role" : { + "Fn::GetAtt" : [ "SingletonLambda0dc9f04c622c4059b399834a2c1ac15fServiceRoleD59355BD", "Arn" ] + }, + "FunctionName" : "CDK Lambda Cron Example", + "Runtime" : "python2.7", + "Description" : "Lambda which prints \"I'm running\"", + "Timeout" : 300, + "Handler" : "index.main", + "Code" : { + "ZipFile" : "def main(event, context):\n print(\"I'm running!\")\n" + } + } + } + } +} \ No newline at end of file