Skip to content

Commit

Permalink
Add ability to specify multiple lambda handlers per project.
Browse files Browse the repository at this point in the history
By specifying a sequence of tuples from lambda function name to handler
method, in the lambdaHandlers setting, you can create or update mulitple
lambda functions with a single create or update operation. When used,
lambdaHandlers overrides the single lambda handler specified through
the lambdaName and handlerName settings, but if lambdaHandlers is
ignored, the plugin behaves exactly as before, hence retaining backwards
compatibility with existing plugin usages.
  • Loading branch information
dickwall committed Oct 29, 2015
1 parent 80ce762 commit 06bc2af
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 25 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ sbt-aws-lambda can be configured using sbt settings, environment variables or by
| region | AWS_REGION | The name of the AWS region to connect to. Defaults to `us-east-1` |
| awsLambdaTimeout | | The Lambda timeout in seconds (1-300). Defaults to AWS default. |
| awsLambdaMemory | | The amount of memory in MB for the Lambda function (128-1536, multiple of 64). Defaults to AWS default. |
| lambdaHandlers | | Sequence of Lambda names to handler functions (for multiple lambda methods per project). Overrides `lambdaName` and `handlerName` if present. |

An example configuration might look like this:


```scala
retrieveManaged := true

enablePlugins(AwsLambdaPlugin)

lambdaHandlers := Seq(
"function1" -> "com.gilt.example.Lambda::handleRequest1",
"function2" -> "com.gilt.example.Lambda::handleRequest2",
"function3" -> "com.gilt.example.OtherLambda::handleRequest3"
)

// or, instead of the above, for just one function/handler
//
// lambdaName := Some("function1")
//
// handlerName := Some("com.gilt.example.Lambda::handleRequest1")

s3Bucket := Some("lambda-jars")

awsLambdaMemory := Some(192)

awsLambdaTimeout := Some(30)

roleArn := Some("arn:aws:iam::123456789000:role/lambda_basic_execution")

```
(note that you will need to use a real ARN for your role rather than copying this one).


Publishing new versions of this plugin
Expand Down
61 changes: 36 additions & 25 deletions src/main/scala/com/gilt/aws/lambda/AwsLambdaPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import sbt._
object AwsLambdaPlugin extends AutoPlugin {

object autoImport {
val createLambda = taskKey[LambdaARN]("Create a new AWS Lambda function from the current project")
val updateLambda = taskKey[LambdaARN]("Package and deploy the current project to an existing AWS Lambda")
val createLambda = taskKey[Seq[LambdaARN]]("Create a new AWS Lambda function from the current project")
val updateLambda = taskKey[Seq[LambdaARN]]("Package and deploy the current project to an existing AWS Lambda")

val s3Bucket = settingKey[Option[String]]("ID of the S3 bucket where the jar will be uploaded")
val lambdaName = settingKey[Option[String]]("Name of the AWS Lambda to update")
Expand All @@ -15,6 +15,7 @@ object AwsLambdaPlugin extends AutoPlugin {
val region = settingKey[Option[String]]("Name of the AWS region to connect to")
val awsLambdaTimeout = settingKey[Option[Int]]("The Lambda timeout length in seconds (1-300)")
val awsLambdaMemory = settingKey[Option[Int]]("The amount of memory in MB for the Lambda function (128-1536, multiple of 64)")
val lambdaHandlers = settingKey[Seq[(String, String)]]("A sequence of pairs of Lambda function names to handlers (for multiple handlers in one jar)")
}

import autoImport._
Expand All @@ -26,59 +27,67 @@ object AwsLambdaPlugin extends AutoPlugin {
region = region.value,
jar = sbtassembly.AssemblyKeys.assembly.value,
s3Bucket = s3Bucket.value,
lambdaName = lambdaName.value
lambdaName = lambdaName.value,
lambdaHandlers = lambdaHandlers.value
),
createLambda := doCreateLambda(
region = region.value,
jar = sbtassembly.AssemblyKeys.assembly.value,
s3Bucket = s3Bucket.value,
lambdaName = lambdaName.value,
handlerName = handlerName.value,
lambdaHandlers = lambdaHandlers.value,
roleArn = roleArn.value,
timeout = awsLambdaTimeout.value,
memory = awsLambdaMemory.value
),
s3Bucket := None,
lambdaName := Some(sbt.Keys.name.value),
handlerName := None,
lambdaHandlers := List.empty[(String, String)],
roleArn := None,
region := Some("us-east-1")
region := Some("us-east-1"),
awsLambdaMemory := None,
awsLambdaTimeout := None
)

private def doUpdateLambda(region: Option[String], jar: File, s3Bucket: Option[String], lambdaName: Option[String]): LambdaARN = {
private def doUpdateLambda(region: Option[String], jar: File, s3Bucket: Option[String], lambdaName: Option[String], lambdaHandlers: Seq[(String, String)]): Seq[LambdaARN] = {
val resolvedRegion = resolveRegion(region)
val resolvedBucketId = resolveBucketId(s3Bucket)
val resolvedLambdaName = resolveLambdaName(lambdaName)
val resolvedLambdaNames = resolveLambdaNames(lambdaName, lambdaHandlers)

AwsS3.pushJarToS3(jar, resolvedBucketId) match {
case Success(s3Key) =>
case Success(s3Key) => for (resolvedLambdaName <- resolvedLambdaNames) yield {
AwsLambda.updateLambda(resolvedRegion, resolvedLambdaName, resolvedBucketId, s3Key) match {
case Success(updateFunctionCodeResult) =>
LambdaARN(updateFunctionCodeResult.getFunctionArn)
case Failure(exception) =>
sys.error(s"Error updating lambda: ${exception.getStackTraceString}")
}
}
case Failure(exception) =>
sys.error(s"Error uploading jar to S3 lambda: ${exception.getStackTraceString}")
}
}

private def doCreateLambda(region: Option[String], jar: File, s3Bucket: Option[String], lambdaName: Option[String], handlerName: Option[String], roleArn: Option[String], timeout: Option[Int], memory: Option[Int]): LambdaARN = {
private def doCreateLambda(region: Option[String], jar: File, s3Bucket: Option[String], lambdaName: Option[String], handlerName: Option[String], lambdaHandlers: Seq[(String, String)], roleArn: Option[String], timeout: Option[Int], memory: Option[Int]): Seq[LambdaARN] = {
val resolvedRegion = resolveRegion(region)
val resolvedLambdaName = resolveLambdaName(lambdaName)
val resolvedHandlerName = resolveHandlerName(handlerName)
val resolvedLambdaNames = resolveLambdaNames(lambdaName, lambdaHandlers)
val resolvedHandlerNames = resolveHandlerNames(handlerName, lambdaHandlers)
val resolvedRoleName = resolveRoleARN(roleArn)
val resolvedBucketId = resolveBucketId(s3Bucket)
val resolvedTimeout = resolveTimeout(timeout)
val resolvedMemory = resolveMemory(memory)

AwsS3.pushJarToS3(jar, resolvedBucketId) match {
case Success(s3Key) =>
AwsLambda.createLambda(resolvedRegion, jar, resolvedLambdaName, resolvedHandlerName, resolvedRoleName, resolvedBucketId, resolvedTimeout, resolvedMemory) match {
case Success(createFunctionCodeResult) =>
LambdaARN(createFunctionCodeResult.getFunctionArn)
case Failure(exception) =>
sys.error(s"Failed to create lambda function: ${exception.getLocalizedMessage}\n${exception.getStackTraceString}")
for ((resolvedLambdaName, resolvedHandlerName) <- resolvedLambdaNames.zip(resolvedHandlerNames)) yield {
AwsLambda.createLambda(resolvedRegion, jar, resolvedLambdaName, resolvedHandlerName, resolvedRoleName, resolvedBucketId, resolvedTimeout, resolvedMemory) match {
case Success(createFunctionCodeResult) =>
LambdaARN(createFunctionCodeResult.getFunctionArn)
case Failure(exception) =>
sys.error(s"Failed to create lambda function: ${exception.getLocalizedMessage}\n${exception.getStackTraceString}")
}
}
case Failure(exception) =>
sys.error(s"Error upload jar to S3 lambda: ${exception.getLocalizedMessage}")
Expand All @@ -105,22 +114,24 @@ object AwsLambdaPlugin extends AutoPlugin {
}
}

private def resolveLambdaName(sbtSettingValueOpt: Option[String]): LambdaName = {
sbtSettingValueOpt match {
case Some(f) => LambdaName(f)
private def resolveLambdaNames(lambdaName: Option[String], lambdaHandlers: Seq[(String, String)]): Seq[LambdaName] = {
if (lambdaHandlers.nonEmpty) lambdaHandlers.map { case (l, h) => LambdaName(l) } else
lambdaName match {
case Some(f) => Seq(LambdaName(f))
case None => sys.env.get(EnvironmentVariables.lambdaName) match {
case Some(envVarFunctionName) => LambdaName(envVarFunctionName)
case None => promptUserForFunctionName()
case Some(envVarFunctionName) => Seq(LambdaName(envVarFunctionName))
case None => Seq(promptUserForFunctionName())
}
}
}

private def resolveHandlerName(sbtSettingValueOpt: Option[String]): HandlerName = {
sbtSettingValueOpt match {
case Some(f) => HandlerName(f)
private def resolveHandlerNames(handlerName: Option[String], lambdaHandlers: Seq[(String, String)]): Seq[HandlerName] = {
if (lambdaHandlers.nonEmpty) lambdaHandlers.map { case(l, h) => HandlerName(h) } else
handlerName match {
case Some(f) => Seq(HandlerName(f))
case None => sys.env.get(EnvironmentVariables.handlerName) match {
case Some(envVarFunctionName) => HandlerName(envVarFunctionName)
case None => promptUserForHandlerName()
case Some(envVarFunctionName) => Seq(HandlerName(envVarFunctionName))
case None => Seq(promptUserForHandlerName())
}
}
}
Expand Down

0 comments on commit 06bc2af

Please sign in to comment.