Search code examples
javaamazon-web-servicesaws-cdkclouddevelopmentkit

How do I reference my lambda from code in AWS Cloud Development Kit?


import software.amazon.awscdk.services.lambda.Function;

Function helloLambda = new Function(helloStack, "hellocdkworld123", FunctionProps.builder()
              .functionName("HelloLambda")
              .code(Code.fromAsset("target/cdkhello-0.1.jar")) // <- x ?
              .runtime(Runtime.JAVA_8)
              .handler("com.myorg.functions.HelloLambda::sayHello") <- y?
              .build());

There is also a possibility to reference it by S3 bucket. But when I run cdk bootstrap I get a generated bucket with generated name of the jar file. How should I be able to reference that before hand from code? Of course now I could write the exact bucket + file but then purpose of defining it from code is lost right?


Solution

  • First of all, assuming that the method that you want to execute when the Lambda is invoked is sayHello, from the com.myorg.functions.HelloLambda class, then that part of your solution is correct. The more difficult part is actually accessing the JAR with your Lambda code in it.

    NOTE: I've updated my original answer with what I think is a better way to accomplish this. In order to avoid confusion and making this answer too wordy, I've removed the original answer, though much of it is common with this one. I credit this answer for helping to improve this answer.

    Pass the path to the dependent resource's JAR to CDK

    TL;DR

    • Create a new property for the full path to your Lambda JAR.
    • Associate dependency and execution related goals into the package phase of the build.
    • Update cdk.json to point to the the package phase.
    • Pass the full path via a system property to your CDK code.
    • Use the System property to pass to Code.asset(...).

    Preparation

    I've separated out the Lambda and the CDK infrastructure code into separate Maven modules. The intention being that once the Lambda code is compiled, packaged up into an uber JAR (its code plus all of its dependencies' code), the infrastructure module can refer to it as a dependency, passing the full path to the Lambda JAR to the App/Stack class to that it can use it as an asset.

    Create a new property for the full path to your Lambda JAR.

    In the properties section of your pom.xml, create a new property to refer to your Lambda JAR. Something like this:

    <properties>
        ...
        <lambda.jar>${GROUP_ID:ARTIFACT_ID:jar}</lambda.jar>
        ...
    </properties>
    

    Populate a property with the full path to your Lambda dependency's JAR, using the dependency plugin.

    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>properties</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
            </plugin>
            ...
        <plugins>
    </build>
    

    This associates the properties goal with the process-resources phase. Whenever that phase of the build occurs, the property you've created previously will be populated with the full path to the JAR in your local repository.

    Associate dependency and execution related goals into a single phase of the build.

    When you create a new CDK Java project, it outputs a file called cdk.json, which points by default to the Maven exec:java goal. In order for your new lambda.jar property to be populated correctly, you need to associate the exec:java goal with the same phase as above.

    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.myorg.TestingApp</mainClass>
                </configuration>
            </plugin>
            ...
        </plugins>
    </build>
    

    In order for your code to get access to the JAR file that you've generated, you need to create a System property (I couldn't get environment variables to work) to your App class. Your pom.xml started with something like this:

    Pass the full path via a system property to your CDK code.

    In the configuration section (after mainClass), add a system property for your assets directory, something like this:

    <systemProperties>
        <systemProperty>
            <key>lambda.jar</key>
            <value>${lambda.jar}</value>
        </systemProperty>
    </systemProperties>
    

    Update cdk.json to point to the the common phase you've used.

    Your cdk.json of your CDK project should be changed to point to the process-resources phase. Once done it will look like this:

    {
      "app": "mvn package"
    }
    

    It will cause both the goals to be run in succession, and upon execution the path to your Lambda's JAR will be passed as a system property.

    Access the property from your App/Stack code.

    Finally, now that the system property is created, you can access it from your code by calling System.getProperty("lambda.jar"). Something like this:

    final Code code = Code.fromAsset(System.getProperty("lambda.jar"));
    

    You can then use the code reference wherever needed when defining your Lambda functions.