Skip to content

Commit 8e40f0c

Browse files
committed
add ingestor, browser, bump eoapi-cdk
1 parent d1f5cfe commit 8e40f0c

File tree

3 files changed

+113
-35
lines changed

3 files changed

+113
-35
lines changed

infrastructure/aws/cdk/app.py

Lines changed: 108 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
import os
55
from typing import Any
66

7+
import boto3
78
from aws_cdk import App, CfnOutput, Duration, RemovalPolicy, Stack, Tags
89
from aws_cdk import aws_ec2 as ec2
10+
from aws_cdk import aws_iam as iam
911
from aws_cdk import aws_lambda
1012
from aws_cdk import aws_logs as logs
1113
from aws_cdk import aws_rds as rds
14+
from aws_cdk import aws_s3 as s3
1215
from config import (
1316
eoAPISettings,
1417
eoDBSettings,
@@ -20,6 +23,8 @@
2023
from eoapi_cdk import (
2124
PgStacApiLambda,
2225
PgStacDatabase,
26+
StacBrowser,
27+
StacIngestor,
2328
TiPgApiLambda,
2429
TitilerPgstacApiLambda,
2530
)
@@ -42,8 +47,6 @@ def __init__( # noqa: C901
4247
"""Define stack."""
4348
super().__init__(scope, id, **kwargs)
4449

45-
# vpc = ec2.Vpc(self, f"{id}-vpc", nat_gateways=0)
46-
4750
vpc = ec2.Vpc(
4851
self,
4952
f"{id}-vpc",
@@ -52,23 +55,8 @@ def __init__( # noqa: C901
5255
name="ingress",
5356
cidr_mask=24,
5457
subnet_type=ec2.SubnetType.PUBLIC,
55-
),
56-
ec2.SubnetConfiguration(
57-
name="application",
58-
cidr_mask=24,
59-
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
60-
),
61-
ec2.SubnetConfiguration(
62-
name="rds",
63-
cidr_mask=28,
64-
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
65-
),
58+
)
6659
],
67-
nat_gateways=1,
68-
)
69-
print(
70-
"""The eoAPI stack use AWS NatGateway for the Raster service so it can reach the internet.
71-
This might incurs some cost (https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)."""
7260
)
7361

7462
interface_endpoints = [
@@ -135,6 +123,9 @@ def __init__( # noqa: C901
135123
secrets_prefix=os.path.join(stage, name),
136124
)
137125

126+
# allow connections from anywhere to the DB
127+
pgstac_db.db.connections.allow_default_port_from_any_ipv4()
128+
138129
CfnOutput(
139130
self,
140131
f"{id}-database-secret-arn",
@@ -174,10 +165,6 @@ def __init__( # noqa: C901
174165
f"{id}-raster-lambda",
175166
db=pgstac_db.db,
176167
db_secret=pgstac_db.pgstac_secret,
177-
vpc=vpc,
178-
subnet_selection=ec2.SubnetSelection(
179-
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
180-
),
181168
api_env=env,
182169
lambda_function_options={
183170
"code": aws_lambda.Code.from_docker_build(
@@ -232,15 +219,11 @@ def __init__( # noqa: C901
232219
if "raster" in eoapi_settings.functions:
233220
env["TITILER_ENDPOINT"] = eoraster.url.strip("/")
234221

235-
PgStacApiLambda(
222+
eostac = PgStacApiLambda(
236223
self,
237224
id=f"{id}-stac-lambda",
238225
db=pgstac_db.db,
239226
db_secret=pgstac_db.pgstac_secret,
240-
vpc=vpc,
241-
subnet_selection=ec2.SubnetSelection(
242-
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
243-
),
244227
api_env=env,
245228
lambda_function_options={
246229
"runtime": aws_lambda.Runtime.PYTHON_3_11,
@@ -259,6 +242,38 @@ def __init__( # noqa: C901
259242
},
260243
)
261244

245+
if eostac_settings.stac_browser_github_tag is not None:
246+
assert (
247+
eostac_settings.stac_api_custom_domain_name is not None
248+
), "stac_api_custom_domain_name must be set if stac_browser_github_tag is not None. The browser deployment needs a resolved STAC API url at deployment time and so needs to rely on a predefined custom domain name."
249+
stac_browser_bucket = s3.Bucket(
250+
self,
251+
"stac-browser-bucket",
252+
bucket_name=f"{id.lower()}-stac-browser",
253+
removal_policy=RemovalPolicy.DESTROY,
254+
auto_delete_objects=True,
255+
website_index_document="index.html",
256+
public_read_access=True,
257+
block_public_access=s3.BlockPublicAccess(
258+
block_public_acls=False,
259+
block_public_policy=False,
260+
ignore_public_acls=False,
261+
restrict_public_buckets=False,
262+
),
263+
object_ownership=s3.ObjectOwnership.OBJECT_WRITER,
264+
)
265+
266+
# need to build this manually, the attribute eostac.url is not resolved yet.
267+
268+
StacBrowser(
269+
self,
270+
"stac-browser",
271+
github_repo_tag=eostac_settings.stac_browser_github_tag,
272+
stac_catalog_url=eostac_settings.stac_api_custom_domain_name,
273+
website_index_document="index.html",
274+
bucket_arn=stac_browser_bucket.bucket_arn,
275+
)
276+
262277
# eoapi.vector
263278
if "vector" in eoapi_settings.functions:
264279
db_secrets = {
@@ -292,12 +307,8 @@ def __init__( # noqa: C901
292307
TiPgApiLambda(
293308
self,
294309
f"{id}-vector-lambda",
295-
vpc=vpc,
296310
db=pgstac_db.db,
297311
db_secret=pgstac_db.pgstac_secret,
298-
subnet_selection=ec2.SubnetSelection(
299-
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
300-
),
301312
api_env=env,
302313
lambda_function_options={
303314
"runtime": aws_lambda.Runtime.PYTHON_3_11,
@@ -316,6 +327,72 @@ def __init__( # noqa: C901
316327
},
317328
)
318329

330+
if "ingestor" in eoapi_settings.functions:
331+
332+
data_access_role = self._create_data_access_role()
333+
334+
stac_ingestor = StacIngestor(
335+
self,
336+
"stac-ingestor",
337+
stac_url=eostac.url,
338+
stage=eoapi_settings.stage,
339+
data_access_role=data_access_role,
340+
stac_db_secret=pgstac_db.pgstac_secret,
341+
stac_db_security_group=pgstac_db.db.connections.security_groups[0],
342+
api_env={"REQUESTER_PAYS": "True"},
343+
)
344+
345+
data_access_role = self._grant_assume_role_with_principal_pattern(
346+
data_access_role, stac_ingestor.handler_role.role_name
347+
)
348+
349+
def _create_data_access_role(self) -> iam.Role:
350+
351+
"""
352+
Creates an IAM role with full S3 read access.
353+
"""
354+
355+
data_access_role = iam.Role(
356+
self,
357+
"data-access-role",
358+
assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"),
359+
)
360+
361+
data_access_role.add_managed_policy(
362+
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess")
363+
)
364+
365+
return data_access_role
366+
367+
def _grant_assume_role_with_principal_pattern(
368+
self,
369+
role_to_assume: iam.Role,
370+
principal_pattern: str,
371+
account_id: str = boto3.client("sts").get_caller_identity().get("Account"),
372+
) -> iam.Role:
373+
"""
374+
Grants assume role permissions to the role of the given
375+
account with the given name pattern. Default account
376+
is the current account.
377+
"""
378+
379+
role_to_assume.assume_role_policy.add_statements(
380+
iam.PolicyStatement(
381+
effect=iam.Effect.ALLOW,
382+
principals=[iam.AnyPrincipal()],
383+
actions=["sts:AssumeRole"],
384+
conditions={
385+
"StringLike": {
386+
"aws:PrincipalArn": [
387+
f"arn:aws:iam::{account_id}:role/{principal_pattern}"
388+
]
389+
}
390+
},
391+
)
392+
)
393+
394+
return role_to_assume
395+
319396

320397
app = App()
321398

infrastructure/aws/cdk/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class functionName(str, Enum):
1212
stac = "stac"
1313
raster = "raster"
1414
vector = "vector"
15+
ingestor = "ingestor"
1516

1617

1718
class eoAPISettings(BaseSettings):
@@ -56,7 +57,8 @@ class eoSTACSettings(BaseSettings):
5657

5758
timeout: int = 10
5859
memory: int = 256
59-
60+
stac_browser_github_tag: None | str = "v3.1.0" # if not none, will try to deploy this version of radiant earth stac browser
61+
stac_api_custom_domain_name: None | str = "https://stac.eoapi.dev"
6062
model_config = {
6163
"env_prefix": "CDK_EOAPI_STAC_",
6264
"env_file": ".env",

infrastructure/aws/requirements-cdk.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ aws-cdk-lib==2.99.1
33
aws_cdk-aws_apigatewayv2_alpha==2.99.1a0
44
aws_cdk-aws_apigatewayv2_integrations_alpha==2.99.1a0
55
constructs>=10.0.0
6-
6+
boto3==1.28.71
77
# pydantic settings
88
pydantic~=2.0
99
pydantic-settings~=2.0
10-
11-
../eoapi-cdk/dist/python/eoapi_cdk-5.4.0-py3-none-any.whl
10+
eoapi-cdk==6.0.0

0 commit comments

Comments
 (0)