Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow passing HandlerFunction to run function directly #20

Merged
merged 1 commit into from
Oct 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Common/index.ts
Original file line number Diff line number Diff line change
@@ -78,3 +78,7 @@ export type HandlerFunction = (
data: IEnvironmentData & IHeaderData,
callback: CallbackFunction
) => PromiseLike<unknown> | unknown;

export function isHandlerFunction(value: any): value is HandlerFunction {
return typeof value === "function";
}
14 changes: 11 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

"use strict";

import { HandlerFunction } from "./Common";
import { HandlerFunction, isHandlerFunction } from "./Common";
import * as Errors from "./Errors";
import RuntimeClient from "./RuntimeClient";
import Runtime from "./Runtime";
@@ -18,7 +18,13 @@ import * as UserFunction from "./utils/UserFunction";

LogPatch.patchConsole();

export function run(appRoot: string, handler: string): void {
export function run(appRoot: string, handler: string): void;
export function run(handler: HandlerFunction): void;

export function run(
appRootOrHandler: string | HandlerFunction,
handler: string = ""
): void {
if (!process.env.AWS_LAMBDA_RUNTIME_API) {
throw new Error("Missing Runtime API Server configuration.");
}
@@ -50,7 +56,9 @@ export function run(appRoot: string, handler: string): void {
BeforeExitListener.reset();
process.on("beforeExit", BeforeExitListener.invoke);

const handlerFunc = UserFunction.load(appRoot, handler) as HandlerFunction;
const handlerFunc = isHandlerFunction(appRootOrHandler)
? appRootOrHandler
: (UserFunction.load(appRootOrHandler, handler) as HandlerFunction);
const runtime = new Runtime(client, handlerFunc, errorCallbacks);

runtime.scheduleIteration();
102 changes: 102 additions & 0 deletions test/integration/codebuild/buildspec.os.alpine.4.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
version: 0.2

env:
variables:
OS_DISTRIBUTION: alpine
NODE_BINARY_LOCATION: "/usr/local/bin/node"
NPX_BINARY_LOCATION: "/usr/local/bin/npx"
batch:
build-matrix:
static:
ignore-failure: false
env:
type: LINUX_CONTAINER
privileged-mode: true
dynamic:
env:
variables:
DISTRO_VERSION:
- "3.12"
RUNTIME_VERSION:
- "12"
- "14"
phases:
pre_build:
commands:
- export IMAGE_TAG="nodejs-${OS_DISTRIBUTION}-${DISTRO_VERSION}:${RUNTIME_VERSION}"
- echo "Extracting and including the Runtime Interface Emulator"
- SCRATCH_DIR=".scratch"
- mkdir "${SCRATCH_DIR}"
- tar -xvf test/integration/resources/aws-lambda-rie.tar.gz --directory "${SCRATCH_DIR}"
- >
cp "test/integration/docker/Dockerfile.programmatic.${OS_DISTRIBUTION}" \
"${SCRATCH_DIR}/Dockerfile.programmatic.${OS_DISTRIBUTION}.tmp"
- >
echo "RUN apk add curl" >> \
"${SCRATCH_DIR}/Dockerfile.programmatic.${OS_DISTRIBUTION}.tmp"
- >
echo "COPY ${SCRATCH_DIR}/aws-lambda-rie /usr/bin/aws-lambda-rie" >> \
"${SCRATCH_DIR}/Dockerfile.programmatic.${OS_DISTRIBUTION}.tmp"
- >
if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
then
echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
else
echo "Performing DockerHub login . . ."
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
fi
- echo "Building image ${IMAGE_TAG}"
- >
docker build . \
-f "${SCRATCH_DIR}/Dockerfile.programmatic.${OS_DISTRIBUTION}.tmp" \
-t "${IMAGE_TAG}" \
--build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
--build-arg DISTRO_VERSION="${DISTRO_VERSION}"
build:
commands:
- set -x
- echo "Running Image ${IMAGE_TAG}"
- docker network create "${OS_DISTRIBUTION}-network"
- >
docker run \
--detach \
-e "NODE_BINARY_LOCATION=${NODE_BINARY_LOCATION}" \
--name "${OS_DISTRIBUTION}-app" \
--network "${OS_DISTRIBUTION}-network" \
--entrypoint="" \
"${IMAGE_TAG}" \
sh -c '/usr/bin/aws-lambda-rie ${NODE_BINARY_LOCATION} index.js'
- sleep 2
- >
docker run \
--name "${OS_DISTRIBUTION}-tester" \
--env "TARGET=${OS_DISTRIBUTION}-app" \
--network "${OS_DISTRIBUTION}-network" \
--entrypoint="" \
"${IMAGE_TAG}" \
sh -c 'curl -X POST "http://${TARGET}:8080/2015-03-31/functions/function/invocations" -d "{}" --max-time 10'
- actual="$(docker logs --tail 1 "${OS_DISTRIBUTION}-tester" | xargs)"
- expected='success'
- |
echo "Response: ${actual}"
if [[ "$actual" != "$expected" ]]; then
echo "fail! runtime: $RUNTIME - expected output $expected - got $actual"
echo "---------Container Logs: ${OS_DISTRIBUTION}-app----------"
echo
docker logs "${OS_DISTRIBUTION}-app"
echo
echo "---------------------------------------------------"
echo "--------Container Logs: ${OS_DISTRIBUTION}-tester--------"
echo
docker logs "${OS_DISTRIBUTION}-tester"
echo
echo "---------------------------------------------------"
exit -1
fi
finally:
- echo "Cleaning up..."
- docker stop "${OS_DISTRIBUTION}-app" || true
- docker rm --force "${OS_DISTRIBUTION}-app" || true
- docker stop "${OS_DISTRIBUTION}-tester" || true
- docker rm --force "${OS_DISTRIBUTION}-tester" || true
- docker network rm "${OS_DISTRIBUTION}-network" || true
52 changes: 52 additions & 0 deletions test/integration/docker/Dockerfile.programmatic.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Define global args
ARG FUNCTION_DIR="/home/app/"
ARG RUNTIME_VERSION
ARG DISTRO_VERSION

# Stage 1 - build function and dependencies
FROM node:${RUNTIME_VERSION}-alpine${DISTRO_VERSION} AS build-image
# Install aws-lambda-cpp build dependencies
RUN apk add --update-cache \
build-base \
libtool \
musl-dev \
libressl-dev \
libffi-dev \
autoconf \
automake \
libexecinfo-dev \
make \
cmake \
python3 \
libcurl

# Include global arg in this stage of the build
ARG FUNCTION_DIR
# Create function directory
RUN mkdir -p ${FUNCTION_DIR}

# Copy & build Runtime Interface Client package (as we're installing it from a local filesystem source)
WORKDIR ${FUNCTION_DIR}/deps/aws-lambda-ric
COPY . .
RUN make build && \
npm run test:unit

# Copy function code
COPY test/integration/test-handlers/programmatic/* ${FUNCTION_DIR}
# Install the function's dependencies
WORKDIR ${FUNCTION_DIR}
RUN npm install


# Stage 2 - final runtime image
# Grab a fresh copy of the Node image
FROM node:${RUNTIME_VERSION}-alpine${DISTRO_VERSION}

# Include global arg in this stage of the build
ARG FUNCTION_DIR
# Set working directory to function root directory
WORKDIR ${FUNCTION_DIR}
# Copy in the built dependencies
COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}

CMD [ "/usr/local/bin/node", "index.js" ]
8 changes: 8 additions & 0 deletions test/integration/test-handlers/programmatic/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const ric = require('aws-lambda-ric');

const echo = async (event, context) => {
console.log('hello world');
return 'success';
};

ric.run(echo);
11 changes: 11 additions & 0 deletions test/integration/test-handlers/programmatic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "programmatic-hanlder",
"version": "1.0.0",
"description": "Sample Lambda echo handler for NodeJS",
"main": "app.js",
"author": "AWS Lambda",
"license": "Apache-2.0",
"dependencies": {
"aws-lambda-ric": "file:deps/aws-lambda-ric"
}
}
21 changes: 21 additions & 0 deletions test/unit/Common/Common.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use strict";

require("should");
import * as Common from "../../../src/Common";

describe("type guards HandlerFunction", () => {
it("should compile the code", () => {
const func = () => {};
if (Common.isHandlerFunction(func)) {
func();
}
});

it("should return true if function", () => {
Common.isHandlerFunction(() => {}).should.be.true();
});

it("should return false if not function", () => {
Common.isHandlerFunction("MyHandler").should.be.false();
});
});