From 8ea46c5fb38d58d10e669d04bc0d0c770d8e8ab0 Mon Sep 17 00:00:00 2001 From: emileten Date: Fri, 1 Sep 2023 18:02:29 +0900 Subject: [PATCH 1/8] add tipg and bump eoapi-cdk --- app.py | 1 + eoapi_template/pgStacInfra.py | 19 +++++++++++++++++++ requirements.txt | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 3f4a4b6..99aea9b 100644 --- a/app.py +++ b/app.py @@ -32,6 +32,7 @@ vpc=vpc_stack.vpc, stac_api_lambda_name=config.build_service_name("STAC API"), titiler_pgstac_api_lambda_name=config.build_service_name("titiler pgSTAC API"), + tipg_api_lambda_name=config.build_service_name("tipg API"), stage=config.stage, db_allocated_storage=config.db_allocated_storage, public_db_subnet=config.public_db_subnet, diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index 140782e..7c1ab99 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -9,6 +9,7 @@ PgStacDatabase, StacIngestor, TitilerPgstacApiLambda, + TiPgApiLambda ) @@ -24,6 +25,7 @@ def __init__( db_instance_type: str, stac_api_lambda_name: str, titiler_pgstac_api_lambda_name: str, + tipg_api_lambda_name: str, bastion_host_allow_ip_list: list, bastion_host_create_elastic_ip: bool, titiler_buckets: list, @@ -81,6 +83,23 @@ def __init__( ), buckets=titiler_buckets, ) + + TiPgApiLambda( + self, + "tipg-api", + api_env={ + "NAME": tipg_api_lambda_name, + "description": f"{stage} tipg API" + }, + vpc=vpc, + db=pgstac_db.db, + db_secret=pgstac_db.pgstac_secret, + subnet_selection=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PUBLIC + if public_db_subnet + else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS + ) + ) BastionHost( self, diff --git a/requirements.txt b/requirements.txt index 7ce3b56..fb7e0dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aws-cdk-lib>=2.75.0 aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 -eoapi-cdk==5.0.0 +eoapi-cdk==5.2.0 constructs>=10.0.0,<11.0.0 pydantic==2.0.2 pydantic-settings==2.0.1 From ff7afa676afda40668eb08cf75d3e9902102c9fa Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 12 Sep 2023 12:18:40 +0900 Subject: [PATCH 2/8] bump --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fb7e0dc..7f49da5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aws-cdk-lib>=2.75.0 aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 -eoapi-cdk==5.2.0 +eoapi-cdk==5.4.0 constructs>=10.0.0,<11.0.0 pydantic==2.0.2 pydantic-settings==2.0.1 From 2a036cf81bcd21ca04eb57c457d0e95560f662bf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 03:20:33 +0000 Subject: [PATCH 3/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- eoapi_template/pgStacInfra.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index 7c1ab99..e219d8b 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -8,8 +8,8 @@ PgStacApiLambda, PgStacDatabase, StacIngestor, + TiPgApiLambda, TitilerPgstacApiLambda, - TiPgApiLambda ) @@ -83,14 +83,11 @@ def __init__( ), buckets=titiler_buckets, ) - + TiPgApiLambda( self, "tipg-api", - api_env={ - "NAME": tipg_api_lambda_name, - "description": f"{stage} tipg API" - }, + api_env={"NAME": tipg_api_lambda_name, "description": f"{stage} tipg API"}, vpc=vpc, db=pgstac_db.db, db_secret=pgstac_db.pgstac_secret, @@ -98,7 +95,7 @@ def __init__( subnet_type=aws_ec2.SubnetType.PUBLIC if public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS - ) + ), ) BastionHost( From d2fb3bb7c99cb39cc34e53917803fb1f49a237ba Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 12 Sep 2023 15:26:43 +0900 Subject: [PATCH 4/8] make bastion host optional and avoid rewriting all the params in app wrapper just use the config as kwargs --- app.py | 38 ++------------- config.py | 27 +++++++++- eoapi_template/pgStacInfra.py | 92 ++++++++++++++++++----------------- eoapi_template/vpc.py | 15 ++++-- 4 files changed, 87 insertions(+), 85 deletions(-) diff --git a/app.py b/app.py index 99aea9b..f86c201 100644 --- a/app.py +++ b/app.py @@ -1,47 +1,17 @@ -import yaml from aws_cdk import App -from config import Config +from config import build_app_config from eoapi_template import pgStacInfra, vpc app = App() -try: - with open("config.yaml") as f: - config = yaml.safe_load(f) - config = ( - {} if config is None else config - ) # if config is empty, set it to an empty dict - config = Config(**config) -except FileNotFoundError: - # if no config at the expected path, using defaults - config = Config() - -vpc_stack = vpc.VpcStack( - tags=config.tags, - scope=app, - id=config.build_service_name("pgSTAC-vpc"), - nat_gateway_count=config.nat_gateway_count, -) +app_config = build_app_config() +vpc_stack = vpc.VpcStack(scope=app, app_config=app_config) pgstac_infra_stack = pgStacInfra.pgStacInfraStack( scope=app, - tags=config.tags, - id=config.build_service_name("pgSTAC-infra"), vpc=vpc_stack.vpc, - stac_api_lambda_name=config.build_service_name("STAC API"), - titiler_pgstac_api_lambda_name=config.build_service_name("titiler pgSTAC API"), - tipg_api_lambda_name=config.build_service_name("tipg API"), - stage=config.stage, - db_allocated_storage=config.db_allocated_storage, - public_db_subnet=config.public_db_subnet, - db_instance_type=config.db_instance_type, - bastion_host_allow_ip_list=config.bastion_host_allow_ip_list, - bastion_host_create_elastic_ip=config.bastion_host_create_elastic_ip, - bastion_host_user_data=yaml.dump(config.bastion_host_user_data), - titiler_buckets=config.titiler_buckets, - data_access_role_arn=config.data_access_role_arn, - auth_provider_jwks_url=config.auth_provider_jwks_url, + app_config=app_config, ) app.synth() diff --git a/config.py b/config.py index f500c40..678665f 100644 --- a/config.py +++ b/config.py @@ -1,6 +1,7 @@ from typing import Any, Dict, List, Optional, Union import pydantic +import yaml from aws_cdk import aws_ec2 from pydantic_core.core_schema import FieldValidationInfo from pydantic_settings import BaseSettings @@ -10,7 +11,7 @@ DEFAULT_NAT_GATEWAY_COUNT = 1 -class Config(BaseSettings): +class AppConfig(BaseSettings): project_id: Optional[str] = pydantic.Field( description="Project ID", default=DEFAULT_PROJECT_ID ) @@ -54,6 +55,12 @@ class Config(BaseSettings): description="Number of NAT gateways to create", default=DEFAULT_NAT_GATEWAY_COUNT, ) + bastion_host: Optional[bool] = pydantic.Field( + description="""Whether to create a bastion host. It can typically + be used to make administrative connections to the database if + `public_db_subnet` is False""", + default=True, + ) bastion_host_create_elastic_ip: Optional[bool] = pydantic.Field( description="Whether to create an elastic IP for the bastion host", default=False, @@ -93,3 +100,21 @@ def validate_nat_gateway_count(cls, v, info: FieldValidationInfo): def build_service_name(self, service_id: str) -> str: return f"{self.project_id}-{self.stage}-{service_id}" + + +def build_app_config() -> AppConfig: + """Builds the AppConfig object from config.yaml file if exists, + otherwise use defaults""" + try: + with open("config.yaml") as f: + print("Loading config from config.yaml") + app_config = yaml.safe_load(f) + app_config = ( + {} if app_config is None else app_config + ) # if config is empty, set it to an empty dict + app_config = AppConfig(**app_config) + except FileNotFoundError: + # if no config at the expected path, using defaults + app_config = AppConfig() + + return app_config diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index e219d8b..9124a52 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -1,6 +1,5 @@ -from typing import Optional, Union - import boto3 +import yaml from aws_cdk import Stack, aws_ec2, aws_iam, aws_rds from constructs import Construct from eoapi_cdk import ( @@ -12,29 +11,23 @@ TitilerPgstacApiLambda, ) +from config import AppConfig + class pgStacInfraStack(Stack): def __init__( self, scope: Construct, - id: str, vpc: aws_ec2.Vpc, - stage: str, - db_allocated_storage: int, - public_db_subnet: bool, - db_instance_type: str, - stac_api_lambda_name: str, - titiler_pgstac_api_lambda_name: str, - tipg_api_lambda_name: str, - bastion_host_allow_ip_list: list, - bastion_host_create_elastic_ip: bool, - titiler_buckets: list, - data_access_role_arn: Optional[str], - auth_provider_jwks_url: Optional[str], - bastion_host_user_data: Union[str, aws_ec2.UserData], + app_config: AppConfig, **kwargs, ) -> None: - super().__init__(scope, id, **kwargs) + super().__init__( + scope, + id=app_config.build_service_name("pgSTAC-infra"), + tags=app_config.tags, + **kwargs, + ) pgstac_db = PgStacDatabase( self, @@ -45,23 +38,26 @@ def __init__( ), vpc_subnets=aws_ec2.SubnetSelection( subnet_type=aws_ec2.SubnetType.PUBLIC - if public_db_subnet + if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_ISOLATED ), - allocated_storage=db_allocated_storage, - instance_type=aws_ec2.InstanceType(db_instance_type), + allocated_storage=app_config.db_allocated_storage, + instance_type=aws_ec2.InstanceType(app_config.db_instance_type), ) stac_api_lambda = PgStacApiLambda( self, "pgstac-api", - api_env={"NAME": stac_api_lambda_name, "description": f"{stage} STAC API"}, + api_env={ + "NAME": app_config.build_service_name("STAC API"), + "description": f"{app_config.stage} STAC API", + }, vpc=vpc, db=pgstac_db.db, db_secret=pgstac_db.pgstac_secret, subnet_selection=aws_ec2.SubnetSelection( subnet_type=aws_ec2.SubnetType.PUBLIC - if public_db_subnet + if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), ) @@ -70,68 +66,74 @@ def __init__( self, "titiler-pgstac-api", api_env={ - "NAME": titiler_pgstac_api_lambda_name, - "description": f"{stage} titiler pgstac API", + "NAME": app_config.build_service_name("titiler pgSTAC API"), + "description": f"{app_config.stage} titiler pgstac API", }, vpc=vpc, db=pgstac_db.db, db_secret=pgstac_db.pgstac_secret, subnet_selection=aws_ec2.SubnetSelection( subnet_type=aws_ec2.SubnetType.PUBLIC - if public_db_subnet + if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), - buckets=titiler_buckets, + buckets=app_config.titiler_buckets, ) TiPgApiLambda( self, "tipg-api", - api_env={"NAME": tipg_api_lambda_name, "description": f"{stage} tipg API"}, + api_env={ + "NAME": app_config.build_service_name("tipg API"), + "description": f"{app_config.stage} tipg API", + }, vpc=vpc, db=pgstac_db.db, db_secret=pgstac_db.pgstac_secret, subnet_selection=aws_ec2.SubnetSelection( subnet_type=aws_ec2.SubnetType.PUBLIC - if public_db_subnet + if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), ) - BastionHost( - self, - "bastion-host", - vpc=vpc, - db=pgstac_db.db, - ipv4_allowlist=bastion_host_allow_ip_list, - user_data=aws_ec2.UserData.custom(bastion_host_user_data) - if bastion_host_user_data - else aws_ec2.UserData.for_linux(), - create_elastic_ip=bastion_host_create_elastic_ip, - ) + if app_config.bastion_host: + BastionHost( + self, + "bastion-host", + vpc=vpc, + db=pgstac_db.db, + ipv4_allowlist=app_config.bastion_host_allow_ip_list, + user_data=aws_ec2.UserData.custom( + yaml.dump(app_config.bastion_host_user_data) + ) + if app_config.bastion_host_user_data is not None + else aws_ec2.UserData.for_linux(), + create_elastic_ip=app_config.bastion_host_create_elastic_ip, + ) - if data_access_role_arn: + if app_config.data_access_role_arn: # importing provided role from arn. # the stac ingestor will try to assume it when called, # so it must be listed in the data access role trust policy. data_access_role = aws_iam.Role.from_role_arn( self, "data-access-role", - role_arn=data_access_role_arn, + role_arn=app_config.data_access_role_arn, ) else: data_access_role = self._create_data_access_role() stac_ingestor_env = {"REQUESTER_PAYS": "True"} - if auth_provider_jwks_url: - stac_ingestor_env["JWKS_URL"] = auth_provider_jwks_url + if app_config.auth_provider_jwks_url: + stac_ingestor_env["JWKS_URL"] = app_config.auth_provider_jwks_url stac_ingestor = StacIngestor( self, "stac-ingestor", stac_url=stac_api_lambda.url, - stage=stage, + stage=app_config.stage, vpc=vpc, data_access_role=data_access_role, stac_db_secret=pgstac_db.pgstac_secret, @@ -145,7 +147,7 @@ def __init__( # we can only do that if the role is created here. # If injecting a role, that role's trust relationship # must be already set up, or set up after this deployment. - if not data_access_role_arn: + if not app_config.data_access_role_arn: data_access_role = self._grant_assume_role_with_principal_pattern( data_access_role, stac_ingestor.handler_role.role_name ) diff --git a/eoapi_template/vpc.py b/eoapi_template/vpc.py index fdc97ff..bc723f4 100644 --- a/eoapi_template/vpc.py +++ b/eoapi_template/vpc.py @@ -1,12 +1,17 @@ from aws_cdk import Stack, aws_ec2 from constructs import Construct +from config import AppConfig + class VpcStack(Stack): - def __init__( - self, scope: Construct, id: str, nat_gateway_count: int, **kwargs - ) -> None: - super().__init__(scope, id, **kwargs) + def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: + super().__init__( + scope, + id=app_config.build_service_name("pgSTAC-vpc"), + tags=app_config.tags, + **kwargs + ) self.vpc = aws_ec2.Vpc( self, @@ -26,7 +31,7 @@ def __init__( cidr_mask=24, ), ], - nat_gateways=nat_gateway_count, + nat_gateways=app_config.nat_gateway_count, ) self.vpc.add_gateway_endpoint( From 9d071a7d9aadbf2e4c3612bfca96e79b7e427e92 Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 12 Sep 2023 16:37:52 +0900 Subject: [PATCH 5/8] readme --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index ce24ced..5b0cf3e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # eoapi-template + Demonstration application showing the use and configuration options of the [eoapi-cdk constructs](https://github.com/developmentseed/eoapi-cdk) on AWS. + +## Requirements + +- python +- docker +- the AWS CDK CLI +- AWS credentials environment variables configured to point to an account. +- **Optional** a `config.yaml` file to override the default deployment settings defined in `config.py`. + +## Installation + +``` +python -m venv .venv +source .venv/bin/activate +python -m pip install -r requirements.txt +``` + +## Deployment + +First, synthesize the app + +``` +cdk synth --all +``` + +Then, deploy + +``` +cdk deploy --all --require-approval never +``` \ No newline at end of file From d0a739eb0bc389c7d6ba4cc22baa999bdf1c504c Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 12 Sep 2023 17:43:50 +0900 Subject: [PATCH 6/8] add CDN options --- config.py | 43 ++++++++++++++++++++++++++++++ eoapi_template/pgStacInfra.py | 50 ++++++++++++++++++++++++++++++++++- requirements.txt | 1 + 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/config.py b/config.py index 678665f..360ab0e 100644 --- a/config.py +++ b/config.py @@ -81,6 +81,32 @@ class AppConfig(BaseSettings): buckets to grant access to the titiler API""", default=[], ) + acm_certificate_arn: Optional[str] = pydantic.Field( + description="""ARN of ACM certificate to use for + custom domain names. If provided, + CDNs are created for all the APIs""", + default=None, + ) + stac_api_custom_domain: Optional[str] = pydantic.Field( + description="""Custom domain name for the STAC API. + Must provide `acm_certificate_arn`""", + default=None, + ) + titiler_pgstac_api_custom_domain: Optional[str] = pydantic.Field( + description="""Custom domain name for the titiler pgstac API. + Must provide `acm_certificate_arn`""", + default=None, + ) + stac_ingestor_api_custom_domain: Optional[str] = pydantic.Field( + description="""Custom domain name for the STAC ingestor API. + Must provide `acm_certificate_arn`""", + default=None, + ) + tipg_api_custom_domain: Optional[str] = pydantic.Field( + description="""Custom domain name for the tipg API. + Must provide `acm_certificate_arn`""", + default=None, + ) @pydantic.field_validator("tags") def default_tags(cls, v, info: FieldValidationInfo): @@ -98,6 +124,23 @@ def validate_nat_gateway_count(cls, v, info: FieldValidationInfo): else: return v + @pydantic.field_validator("acm_certificate_arn") + def validate_acm_certificate_arn(cls, v, info: FieldValidationInfo): + if v is None and any( + [ + info.data["stac_api_custom_domain"], + info.data["titiler_pgstac_api_custom_domain"], + info.data["stac_ingestor_api_custom_domain"], + info.data["tipg_api_custom_domain"], + ] + ): + raise ValueError( + """If any custom domain is provided, + an ACM certificate ARN must be provided""" + ) + else: + return v + def build_service_name(self, service_id: str) -> str: return f"{self.project_id}-{self.stage}-{service_id}" diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index 9124a52..7aeeeb5 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -1,6 +1,8 @@ import boto3 import yaml -from aws_cdk import Stack, aws_ec2, aws_iam, aws_rds +from aws_cdk import Stack, aws_certificatemanager, aws_ec2, aws_iam, aws_rds +from aws_cdk.aws_apigateway import DomainNameOptions +from aws_cdk.aws_apigatewayv2_alpha import DomainName from constructs import Construct from eoapi_cdk import ( BastionHost, @@ -60,6 +62,18 @@ def __init__( if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), + stac_api_domain_name=DomainName( + self, + "stac-api-domain-name", + domain_name=app_config.stac_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "stac-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.stac_api_custom_domain + else None, ) TitilerPgstacApiLambda( @@ -78,6 +92,18 @@ def __init__( else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), buckets=app_config.titiler_buckets, + titiler_pgstac_api_domain_name=DomainName( + self, + "titiler-pgstac-api-domain-name", + domain_name=app_config.titiler_pgstac_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "titiler-pgstac-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.titiler_pgstac_api_custom_domain + else None, ) TiPgApiLambda( @@ -95,6 +121,18 @@ def __init__( if app_config.public_db_subnet else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), + tipg_api_domain_name=DomainName( + self, + "tipg-api-domain-name", + domain_name=app_config.tipg_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "tipg-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.tipg_api_custom_domain + else None, ) if app_config.bastion_host: @@ -142,6 +180,16 @@ def __init__( subnet_type=aws_ec2.SubnetType.PRIVATE_WITH_EGRESS ), api_env=stac_ingestor_env, + ingestor_domain_name_options=DomainNameOptions( + domain_name=app_config.stac_ingestor_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "stac-ingestor-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.stac_ingestor_api_custom_domain + else None, ) # we can only do that if the role is created here. diff --git a/requirements.txt b/requirements.txt index 7f49da5..50ae0b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ aws-cdk-lib>=2.75.0 aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 +aws-cdk.aws-apigatewayv2-alpha==2.95.1a0 eoapi-cdk==5.4.0 constructs>=10.0.0,<11.0.0 pydantic==2.0.2 From 7d52384e9dccb9dce53ad472bbcb47ef250a576d Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 12 Sep 2023 17:59:45 +0900 Subject: [PATCH 7/8] add basic browser --- config.py | 17 +++++++++++++++++ eoapi_template/pgStacInfra.py | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/config.py b/config.py index 360ab0e..7c4bdf6 100644 --- a/config.py +++ b/config.py @@ -107,6 +107,13 @@ class AppConfig(BaseSettings): Must provide `acm_certificate_arn`""", default=None, ) + stac_browser_version: Optional[str] = pydantic.Field( + description="""Version of the Radiant Earth STAC browser to deploy. + If none provided, no STAC browser will be deployed. + If provided, `stac_api_custom_domain` must be provided + as it will be used as a backend.""", + default=None, + ) @pydantic.field_validator("tags") def default_tags(cls, v, info: FieldValidationInfo): @@ -124,6 +131,16 @@ def validate_nat_gateway_count(cls, v, info: FieldValidationInfo): else: return v + @pydantic.field_validator("stac_browser_version") + def validate_stac_browser_version(cls, v, info: FieldValidationInfo): + if v is not None and info.data["stac_api_custom_domain"] is None: + raise ValueError( + """If a STAC browser version is provided, + a custom domain must be provided for the STAC API""" + ) + else: + return v + @pydantic.field_validator("acm_certificate_arn") def validate_acm_certificate_arn(cls, v, info: FieldValidationInfo): if v is None and any( diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index 7aeeeb5..b0974d6 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -8,6 +8,7 @@ BastionHost, PgStacApiLambda, PgStacDatabase, + StacBrowser, StacIngestor, TiPgApiLambda, TitilerPgstacApiLambda, @@ -192,6 +193,15 @@ def __init__( else None, ) + if app_config.stac_browser_version: + StacBrowser( + self, + "stac-browser", + github_repo_tag=app_config.stac_browser_version, + stac_catalog_url=f"https://{app_config.stac_api_custom_domain}", + website_index_document="index.html", + ) + # we can only do that if the role is created here. # If injecting a role, that role's trust relationship # must be already set up, or set up after this deployment. From f8fc6ac24d40b1704b60bf76c500d36c5a79c0f8 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 13 Sep 2023 15:26:31 +0900 Subject: [PATCH 8/8] make bucket public --- eoapi_template/pgStacInfra.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/eoapi_template/pgStacInfra.py b/eoapi_template/pgStacInfra.py index b0974d6..0f12e8d 100644 --- a/eoapi_template/pgStacInfra.py +++ b/eoapi_template/pgStacInfra.py @@ -1,6 +1,14 @@ import boto3 import yaml -from aws_cdk import Stack, aws_certificatemanager, aws_ec2, aws_iam, aws_rds +from aws_cdk import ( + RemovalPolicy, + Stack, + aws_certificatemanager, + aws_ec2, + aws_iam, + aws_rds, + aws_s3, +) from aws_cdk.aws_apigateway import DomainNameOptions from aws_cdk.aws_apigatewayv2_alpha import DomainName from constructs import Construct @@ -194,12 +202,29 @@ def __init__( ) if app_config.stac_browser_version: + stac_browser_bucket = aws_s3.Bucket( + self, + "stac-browser-bucket", + bucket_name=app_config.build_service_name("stac-browser"), + removal_policy=RemovalPolicy.DESTROY, + auto_delete_objects=True, + website_index_document="index.html", + public_read_access=True, + block_public_access=aws_s3.BlockPublicAccess( + block_public_acls=False, + block_public_policy=False, + ignore_public_acls=False, + restrict_public_buckets=False, + ), + object_ownership=aws_s3.ObjectOwnership.OBJECT_WRITER, + ) StacBrowser( self, "stac-browser", github_repo_tag=app_config.stac_browser_version, stac_catalog_url=f"https://{app_config.stac_api_custom_domain}", website_index_document="index.html", + bucket_arn=stac_browser_bucket.bucket_arn, ) # we can only do that if the role is created here.