Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CloudFormation template. #435

Merged
merged 5 commits into from
Sep 2, 2024
Merged

Update CloudFormation template. #435

merged 5 commits into from
Sep 2, 2024

Conversation

bdon
Copy link
Member

@bdon bdon commented Aug 27, 2024

Updates to make the CF template as one-click as possible.

Not yet inlining the Lambda zip, that will come in a future commit.

Comments inline

@Honricris @charliemcgrady


PublicHostname:
Description: 'The public custom domain name for your CloudFront distribution'
Description: 'Optional. Replace *.cloudfront.net in TileJSON with a custom hostname (e.g. example.com). See docs on how this value is cached.'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See CloudFront function below for explanation on how this works.

The end result here is that the PublicHostname is optional - if you leave it blank the TileJSON will be filled in with the *.cloudfront.net domain name.

- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- PolicyName: S3AccessPolicy
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}:log-stream:*"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scope the resource more closely instead of using *

# ##########################################################################
# # CloudFront::Distribution #
# ##########################################################################
ViewerRequestCloudFrontFunction:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary in the case where PublicHostname parameter is left blank.

By default, the Lambda has no way to know what the CloudFront distribution's domain name is.

One way to do this is to set PUBLIC_HOSTNAME to reference the CloudFront resource in the CF template, but this introduces a circular dependency between the Lambda and the Distribution.

Some hacks to remove circular dependencies like listed in https://theburningmonk.com/2022/05/how-to-work-around-cloudformation-circular-dependencies/ such as creating the Lambda function ARN by hand don't work; the Lambda Function URL checks on provisioning the referenced function exists.

So instead, what we do is we check for the PublicHostname parameter, if blank we install a CloudFront Function that simply attaches the distribution domain name as the x-distribution-domain-name header that the Lambda can then read from.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The code for reading x-distribution-domain-name is not yet in this PR)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside of adding CF Functions is that it costs $0.1 per million requests that go into CloudFront (very little)

ViewerProtocolPolicy: redirect-to-https
CachePolicyId: !Ref CachePolicyId
ResponseHeadersPolicyId: !Ref ResponseHeadersPolicyId
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardcoded to AllViewerExceptHostHeader as described in the docs:

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html

read x-distribution-name header in TileJSON response if PUBLIC_HOSTNAME is not set.
@bdon
Copy link
Member Author

bdon commented Aug 28, 2024

Here is the full output YAML with inlined Lambda code, 494 lines after minification:

https://gist.github.com/bdon/ce3180498561fb0904b7d7ac6df441de

serverless/aws/protomaps-template.yaml Show resolved Hide resolved
headers["Content-Type"] = "application/json";

const t = tileJSON(
header,
await p.getMetadata(),
process.env.PUBLIC_HOSTNAME,
process.env.PUBLIC_HOSTNAME || event.headers['x-distribution-domain-name'] || "",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prior to this change, this function was returning a 501 if no public hostname could be resolved. Here, you're returning an empty string.

Besides notifying a user their setup may not be configured correctly, are there any other reason to avoid returning an empty string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably go back to giving a 501 if both the PUBLIC_HOSTNAME and the distribution domain name header are both blank.

},
"private": true,
"scripts": {
"tsc": "tsc --noEmit --watch",
"build": "esbuild src/index.ts --target=es2020 --outfile=dist/index.mjs --format=esm --bundle --platform=node --target=node18 --external:@aws-sdk/client-s3 --external:@aws-sdk/node-http-handler --banner:js=//$(git describe --always) && cd dist && zip lambda_function.zip index.mjs",
"build": "esbuild src/index.ts --target=es2020 --minify --outfile=dist/index.js --format=cjs --bundle --platform=node --target=node20 --external:@aws-sdk/client-s3 --external:@aws-sdk/node-http-handler --banner:js=//sha:$(git describe --always)",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts on including the template as part of the distribution for PMTiles?

It would be great to be able load the compiled yaml directly in JS ('import { pmtilesCFN } from "pmtiles"'). This could be used alongside CfnInclude to embed this infra in existing CDK stacks. Otherwise, clients will need to manually copy the template.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean as part of the pmtiles NPM package, that can be included as a AWS CDK dependency?

Originally I kept the Cloudflare and AWS specific code out of the main JS package to reduce versioning churn and dependencies, but since those are stabilizing we can consider this in a next major version.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, any NPM package can be used in typescript based CDK projects.

@bdon bdon marked this pull request as ready for review September 2, 2024 07:01
@bdon bdon merged commit bcd5571 into main Sep 2, 2024
2 checks passed
@bdon bdon deleted the cloudformation-updates branch September 2, 2024 07:10
@bdon
Copy link
Member Author

bdon commented Sep 2, 2024

Build output URL: https://pmtiles.io/cloudformation-stack.yaml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants