4
4
import os
5
5
from typing import Any
6
6
7
+ import boto3
7
8
from aws_cdk import App , CfnOutput , Duration , RemovalPolicy , Stack , Tags
8
9
from aws_cdk import aws_ec2 as ec2
10
+ from aws_cdk import aws_iam as iam
9
11
from aws_cdk import aws_lambda
10
12
from aws_cdk import aws_logs as logs
11
13
from aws_cdk import aws_rds as rds
14
+ from aws_cdk import aws_s3 as s3
12
15
from config import (
13
16
eoAPISettings ,
14
17
eoDBSettings ,
20
23
from eoapi_cdk import (
21
24
PgStacApiLambda ,
22
25
PgStacDatabase ,
26
+ StacBrowser ,
27
+ StacIngestor ,
23
28
TiPgApiLambda ,
24
29
TitilerPgstacApiLambda ,
25
30
)
@@ -42,8 +47,6 @@ def __init__( # noqa: C901
42
47
"""Define stack."""
43
48
super ().__init__ (scope , id , ** kwargs )
44
49
45
- # vpc = ec2.Vpc(self, f"{id}-vpc", nat_gateways=0)
46
-
47
50
vpc = ec2 .Vpc (
48
51
self ,
49
52
f"{ id } -vpc" ,
@@ -52,23 +55,8 @@ def __init__( # noqa: C901
52
55
name = "ingress" ,
53
56
cidr_mask = 24 ,
54
57
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
+ )
66
59
],
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)."""
72
60
)
73
61
74
62
interface_endpoints = [
@@ -135,6 +123,9 @@ def __init__( # noqa: C901
135
123
secrets_prefix = os .path .join (stage , name ),
136
124
)
137
125
126
+ # allow connections from anywhere to the DB
127
+ pgstac_db .db .connections .allow_default_port_from_any_ipv4 ()
128
+
138
129
CfnOutput (
139
130
self ,
140
131
f"{ id } -database-secret-arn" ,
@@ -174,10 +165,6 @@ def __init__( # noqa: C901
174
165
f"{ id } -raster-lambda" ,
175
166
db = pgstac_db .db ,
176
167
db_secret = pgstac_db .pgstac_secret ,
177
- vpc = vpc ,
178
- subnet_selection = ec2 .SubnetSelection (
179
- subnet_type = ec2 .SubnetType .PRIVATE_WITH_EGRESS
180
- ),
181
168
api_env = env ,
182
169
lambda_function_options = {
183
170
"code" : aws_lambda .Code .from_docker_build (
@@ -232,15 +219,11 @@ def __init__( # noqa: C901
232
219
if "raster" in eoapi_settings .functions :
233
220
env ["TITILER_ENDPOINT" ] = eoraster .url .strip ("/" )
234
221
235
- PgStacApiLambda (
222
+ eostac = PgStacApiLambda (
236
223
self ,
237
224
id = f"{ id } -stac-lambda" ,
238
225
db = pgstac_db .db ,
239
226
db_secret = pgstac_db .pgstac_secret ,
240
- vpc = vpc ,
241
- subnet_selection = ec2 .SubnetSelection (
242
- subnet_type = ec2 .SubnetType .PRIVATE_WITH_EGRESS
243
- ),
244
227
api_env = env ,
245
228
lambda_function_options = {
246
229
"runtime" : aws_lambda .Runtime .PYTHON_3_11 ,
@@ -259,6 +242,38 @@ def __init__( # noqa: C901
259
242
},
260
243
)
261
244
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
+
262
277
# eoapi.vector
263
278
if "vector" in eoapi_settings .functions :
264
279
db_secrets = {
@@ -292,12 +307,8 @@ def __init__( # noqa: C901
292
307
TiPgApiLambda (
293
308
self ,
294
309
f"{ id } -vector-lambda" ,
295
- vpc = vpc ,
296
310
db = pgstac_db .db ,
297
311
db_secret = pgstac_db .pgstac_secret ,
298
- subnet_selection = ec2 .SubnetSelection (
299
- subnet_type = ec2 .SubnetType .PRIVATE_WITH_EGRESS
300
- ),
301
312
api_env = env ,
302
313
lambda_function_options = {
303
314
"runtime" : aws_lambda .Runtime .PYTHON_3_11 ,
@@ -316,6 +327,72 @@ def __init__( # noqa: C901
316
327
},
317
328
)
318
329
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
+
319
396
320
397
app = App ()
321
398
0 commit comments