Skip to content

Commit 3aaedae

Browse files
authored
feat: custom runtimes option for titiler and ingestor (#66)
* custom runtimes option for titiler and ingestor * make those optional * switch to pythonFunction for titiler runtime, remove boto3 from requirements, a coupl adjustments accordingly, fix optional env vars * remove ref to duration * put back the duration * wrap the python lambda settings that are optional in an interface to make things clearer and more configurable, add bundling
1 parent a94cf94 commit 3aaedae

File tree

7 files changed

+171
-59
lines changed

7 files changed

+171
-59
lines changed

lib/ingestor-api/index.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
RemovalPolicy,
1212
Stack,
1313
} from "aws-cdk-lib";
14-
import { PythonFunction } from "@aws-cdk/aws-lambda-python-alpha";
14+
import { PythonFunction, PythonFunctionProps } from "@aws-cdk/aws-lambda-python-alpha";
1515
import { Construct } from "constructs";
1616

1717
export class StacIngestor extends Construct {
@@ -55,6 +55,7 @@ export class StacIngestor extends Construct {
5555
dbVpc: props.vpc,
5656
dbSecurityGroup: props.stacDbSecurityGroup,
5757
subnetSelection: props.subnetSelection,
58+
apiCode: props.apiCode,
5859
});
5960

6061
this.buildApiEndpoint({
@@ -72,6 +73,7 @@ export class StacIngestor extends Construct {
7273
dbVpc: props.vpc,
7374
dbSecurityGroup: props.stacDbSecurityGroup,
7475
subnetSelection: props.subnetSelection,
76+
ingestorCode: props.ingestorCode,
7577
});
7678

7779
this.registerSsmParameter({
@@ -108,11 +110,17 @@ export class StacIngestor extends Construct {
108110
dbVpc: ec2.IVpc;
109111
dbSecurityGroup: ec2.ISecurityGroup;
110112
subnetSelection: ec2.SubnetSelection
113+
apiCode?: ApiCode;
111114
}): PythonFunction {
112115

113-
const handler = new PythonFunction(this, "api-handler", {
116+
const apiCode = props.apiCode || {
114117
entry: `${__dirname}/runtime`,
115118
index: "src/handler.py",
119+
handler: "handler",
120+
};
121+
122+
const handler = new PythonFunction(this, "api-handler", {
123+
...apiCode,
116124
runtime: lambda.Runtime.PYTHON_3_9,
117125
timeout: Duration.seconds(30),
118126
environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env },
@@ -145,10 +153,19 @@ export class StacIngestor extends Construct {
145153
dbVpc: ec2.IVpc;
146154
dbSecurityGroup: ec2.ISecurityGroup;
147155
subnetSelection: ec2.SubnetSelection;
156+
ingestorCode?: IngestorCode;
148157
}): PythonFunction {
149-
const handler = new PythonFunction(this, "stac-ingestor", {
158+
159+
160+
161+
const ingestorCode = props.ingestorCode || {
150162
entry: `${__dirname}/runtime`,
151163
index: "src/ingestor.py",
164+
handler: "handler",
165+
};
166+
167+
const handler = new PythonFunction(this, "stac-ingestor", {
168+
...ingestorCode,
152169
runtime: lambda.Runtime.PYTHON_3_9,
153170
timeout: Duration.seconds(180),
154171
environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env },
@@ -290,4 +307,56 @@ export interface StacIngestorProps {
290307
* Custom Domain Name Options for Ingestor API
291308
*/
292309
readonly ingestorDomainNameOptions?: apigateway.DomainNameOptions;
310+
311+
/**
312+
* Custom code for the ingestor api.
313+
*
314+
* @default - default in the runtime folder.
315+
*/
316+
readonly apiCode?: ApiCode;
317+
318+
/**
319+
* Custom code for the ingestor.
320+
*
321+
* @default - default in the runtime folder.
322+
*/
323+
readonly ingestorCode?: IngestorCode;
293324
}
325+
326+
export interface ApiCode {
327+
328+
/**
329+
* Path to the source of the function or the location for dependencies, for the api lambda.
330+
*/
331+
readonly entry: PythonFunctionProps["entry"];
332+
333+
/**
334+
* Path to the index file containing the exported handler, relative to `api_lambda_entry`.
335+
*/
336+
readonly index: PythonFunctionProps["index"];
337+
338+
/**
339+
* The name of the exported handler in the `api_lambda_index` file.
340+
*/
341+
readonly handler: PythonFunctionProps["handler"];
342+
343+
}
344+
345+
export interface IngestorCode {
346+
347+
/**
348+
* Path to the source of the function or the location for dependencies, for the ingestor lambda.
349+
*/
350+
readonly entry: PythonFunctionProps["entry"];
351+
352+
/**
353+
* Path to the index file containing the exported handler, relative to `ingestor_lambda_entry`.
354+
*/
355+
readonly index: PythonFunctionProps["index"];
356+
357+
/**
358+
* The name of the exported handler in the `ingestor_lambda_index` file.
359+
*/
360+
readonly handler: PythonFunctionProps["handler"];
361+
362+
}

lib/tipg-api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ import {
8787
readonly dbSecret: secretsmanager.ISecret;
8888

8989
/**
90-
* Custom code to run for fastapi-pgstac.
90+
* Custom code to run for the application.
9191
*
92-
* @default - simplified version of fastapi-pgstac
92+
* @default - simplified version of tipg.
9393
*/
9494
readonly apiCode?: TiPgApiEntrypoint;
9595

lib/titiler-pgstac-api/index.ts

Lines changed: 93 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,51 +8,61 @@ import {
88
CfnOutput,
99
Duration,
1010
aws_logs,
11+
BundlingOptions
1112
} from "aws-cdk-lib";
13+
import { Runtime } from 'aws-cdk-lib/aws-lambda';
14+
import {PythonFunction} from "@aws-cdk/aws-lambda-python-alpha";
1215
import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha";
1316
import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
1417
import { Construct } from "constructs";
1518

19+
20+
// default settings that can be overridden by the user-provided environment.
21+
let defaultTitilerPgstacEnv :{ [key: string]: any } = {
22+
"CPL_VSIL_CURL_ALLOWED_EXTENSIONS": ".tif,.TIF,.tiff",
23+
"GDAL_CACHEMAX": "200",
24+
"GDAL_DISABLE_READDIR_ON_OPEN": "EMPTY_DIR",
25+
"GDAL_INGESTED_BYTES_AT_OPEN": "32768",
26+
"GDAL_HTTP_MERGE_CONSECUTIVE_RANGES": "YES",
27+
"GDAL_HTTP_MULTIPLEX": "YES",
28+
"GDAL_HTTP_VERSION": "2",
29+
"PYTHONWARNINGS": "ignore",
30+
"VSI_CACHE": "TRUE",
31+
"VSI_CACHE_SIZE": "5000000",
32+
"DB_MIN_CONN_SIZE": "1",
33+
"DB_MAX_CONN_SIZE": "1"
34+
}
35+
1636
export class TitilerPgstacApiLambda extends Construct {
1737
readonly url: string;
1838
public titilerPgstacLambdaFunction: lambda.Function;
1939

2040
constructor(scope: Construct, id: string, props: TitilerPgStacApiLambdaProps) {
2141
super(scope, id);
22-
23-
const titilerPgstacEnv = {
24-
"CPL_VSIL_CURL_ALLOWED_EXTENSIONS": ".tif,.TIF,.tiff",
25-
"GDAL_CACHEMAX": "200",
26-
"GDAL_DISABLE_READDIR_ON_OPEN": "EMPTY_DIR",
27-
"GDAL_INGESTED_BYTES_AT_OPEN": "32768",
28-
"GDAL_HTTP_MERGE_CONSECUTIVE_RANGES": "YES",
29-
"GDAL_HTTP_MULTIPLEX": "YES",
30-
"GDAL_HTTP_VERSION": "2",
31-
"PYTHONWARNINGS": "ignore",
32-
"VSI_CACHE": "TRUE",
33-
"VSI_CACHE_SIZE": "5000000",
34-
"DB_MIN_CONN_SIZE": "1",
35-
"DB_MAX_CONN_SIZE": "1",
36-
"PGSTAC_SECRET_ARN": props.dbSecret.secretArn,
37-
}
38-
39-
40-
this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", {
41-
handler: "handler.handler",
42+
43+
44+
// if user provided environment variables, merge them with the defaults.
45+
const apiEnv = props.apiEnv ? { ...defaultTitilerPgstacEnv, ...props.apiEnv, "PGSTAC_SECRET_ARN": props.dbSecret.secretArn } : defaultTitilerPgstacEnv;
46+
47+
const pythonLambdaOptions: TitilerPgstacPythonLambdaOptions = props.pythonLambdaOptions ?? {
4248
runtime: lambda.Runtime.PYTHON_3_10,
43-
code: lambda.Code.fromDockerBuild(__dirname, {
44-
file: "runtime/Dockerfile",
45-
buildArgs: { PYTHON_VERSION: '3.10' },
46-
}),
47-
timeout: Duration.seconds(30),
49+
entry: `${__dirname}/runtime`,
50+
index: "src/handler.py",
51+
handler: "handler",
52+
memorySize: 3008,
53+
architecture: lambda.Architecture.X86_64
54+
}
55+
56+
this.titilerPgstacLambdaFunction = new PythonFunction(this, "titiler-pgstac-api", {
57+
...pythonLambdaOptions,
58+
environment: apiEnv,
4859
vpc: props.vpc,
4960
vpcSubnets: props.subnetSelection,
5061
allowPublicSubnet: true,
51-
memorySize: 3008,
5262
logRetention: aws_logs.RetentionDays.ONE_WEEK,
53-
environment: titilerPgstacEnv,
54-
});
55-
63+
timeout: Duration.seconds(30)
64+
})
65+
5666
// grant access to buckets using addToRolePolicy
5767
if (props.buckets) {
5868
props.buckets.forEach(bucket => {
@@ -105,8 +115,9 @@ import {
105115
readonly dbSecret: secretsmanager.ISecret;
106116

107117
/**
108-
* Customized environment variables to send to titiler-pgstac runtime.
109-
*/
118+
* Customized environment variables to send to titiler-pgstac runtime. These will be merged with `defaultTitilerPgstacEnv`.
119+
* The database secret arn is automatically added to the environment variables at deployment.
120+
/*/
110121
readonly apiEnv?: Record<string, string>;
111122

112123
/**
@@ -116,6 +127,57 @@ import {
116127

117128
/**
118129
* Custom Domain Name Options for Titiler Pgstac API,
130+
*
131+
* @default - undefined.
119132
*/
120133
readonly titilerPgstacApiDomainName?: IDomainName;
134+
135+
/**
136+
* Optional settings for the titiler-pgstac python lambda function.
137+
*
138+
* @default - defined in the construct.
139+
*/
140+
readonly pythonLambdaOptions?: TitilerPgstacPythonLambdaOptions;
141+
142+
}
143+
144+
145+
export interface TitilerPgstacPythonLambdaOptions {
146+
147+
/**
148+
* Path to the source of the function or the location for dependencies.
149+
*/
150+
readonly entry: string;
151+
/**
152+
* The runtime environment. Only runtimes of the Python family are
153+
* supported.
154+
*/
155+
readonly runtime: Runtime;
156+
157+
/**
158+
* The path (relative to entry) to the index file containing the exported handler.
159+
*
160+
*/
161+
readonly index: string;
162+
/**
163+
* The name of the exported handler in the index file.
164+
*/
165+
readonly handler: string;
166+
167+
/**
168+
* Bundling options to use for this function. Use this to specify custom bundling options like
169+
* the bundling Docker image, asset hash type, custom hash, architecture, etc.
170+
*/
171+
readonly bundling?: BundlingOptions;
172+
173+
/**
174+
* The amount of memory, in MB, that is allocated to your Lambda function.
175+
*/
176+
readonly memorySize: number;
177+
178+
/**
179+
* The system architectures compatible with this lambda function.
180+
*/
181+
readonly architecture: lambda.Architecture;
182+
121183
}

lib/titiler-pgstac-api/runtime/Dockerfile

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
uvicorn
1+
uvicorn
2+
boto3>=1.26.139
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
titiler.pgstac==0.5.1
2-
boto3>=1.26.139
32
psycopg[binary, pool]
3+
mangum>=0.14,<0.15

lib/titiler-pgstac-api/runtime/src/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import asyncio
66
import os
77
from mangum import Mangum
8-
from utils import get_secret_dict
8+
from src.utils import get_secret_dict
99

1010
pgstac_secret_arn = os.environ["PGSTAC_SECRET_ARN"]
1111

0 commit comments

Comments
 (0)