app/collectors/lambda.scala (105 lines of code) (raw):

package collectors import agent._ import software.amazon.awssdk.services.lambda.LambdaClient import software.amazon.awssdk.services.lambda.model.{ FunctionConfiguration, ListTagsRequest, PackageType, Runtime } import conf.AWS import controllers.routes import play.api.mvc.Call import utils.Logging import scala.jdk.CollectionConverters._ import scala.language.postfixOps class LambdaCollectorSet(accounts: Accounts) extends CollectorSet[Lambda]( ResourceType("lambda"), accounts, Some(Regional) ) { val lookupCollector: PartialFunction[Origin, Collector[Lambda]] = { case amazon: AmazonOrigin => AWSLambdaCollector(amazon, resource, amazon.crawlRate(resource.name)) } } case class AWSLambdaCollector( origin: AmazonOrigin, resource: ResourceType, crawlRate: CrawlRate ) extends Collector[Lambda] with Logging { val client = LambdaClient.builder .credentialsProvider(origin.credentials.provider) .region(origin.awsRegionV2) .overrideConfiguration(AWS.clientConfig) .build def crawl: Iterable[Lambda] = { client.listFunctionsPaginator().asScala.flatMap(_.functions.asScala).map { lambda => val tags = client .listTags(ListTagsRequest.builder.resource(lambda.functionArn).build) .tags .asScala .toMap Thread.sleep(100) // this avoids ThrottlingException back from AWS Lambda.fromApiData( lambda, origin.region, tags ) } } } object Lambda extends Logging { private def getRuntime(lambda: FunctionConfiguration): Option[String] = { if (lambda.runtime == Runtime.UNKNOWN_TO_SDK_VERSION) { log.warn( s"Lambda runtime ${lambda.runtimeAsString} isn't recognised in the AWS SDK. Is there a later version of the AWS SDK available?" ) } /* From the docs: If the service returns an enum value that is not available in the current SDK version, runtime will return Runtime.UNKNOWN_TO_SDK_VERSION. The raw value returned by the service is available from runtimeAsString. That is `runtimeAsString` is the safest string representation of a lambda runtime as `Runtime.UNKNOWN_TO_SDK_VERSION.toString` yields a NPE. See: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/FunctionConfiguration.html#runtimeAsString-- */ Option(lambda.runtimeAsString) // remove `null`s } private def getPackageType(lambda: FunctionConfiguration): Option[String] = { if (lambda.packageType == PackageType.UNKNOWN_TO_SDK_VERSION) { log.warn( s"Lambda package type ${lambda.packageTypeAsString} isn't recognised in the AWS SDK. Is there a later version of the AWS SDK available?" ) } Option(lambda.packageTypeAsString) // remove `null`s } def fromApiData( lambda: FunctionConfiguration, region: String, tags: Map[String, String] ): Lambda = Lambda( arn = lambda.functionArn(), name = lambda.functionName, region, runtime = getRuntime(lambda), packageType = getPackageType(lambda), tags, app = tags.get("App").map(_.split(",").toList).getOrElse(Nil), guCdkVersion = tags.get("gu:cdk:version"), guCdkPatternName = tags.get("gu:cdk:pattern-name"), stage = tags.get("Stage"), stack = tags.get("Stack") ) } case class Lambda( arn: String, name: String, region: String, runtime: Option[String], packageType: Option[String], tags: Map[String, String], override val app: List[String], override val guCdkVersion: Option[String], override val guCdkPatternName: Option[String], override val stage: Option[String], override val stack: Option[String] ) extends IndexedItemWithCoreTags { override val awsRuntime: String = "Lambda" override def callFromArn: (String) => Call = arn => routes.Api.instance(arn) }