From 283b7b17735b92239f822135f6f07996e0073640 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 28 Aug 2023 16:19:55 +0000 Subject: [PATCH] Deployment template & documentation updates --- README.md | 159 +------------ sam/cloudfront/README.md | 68 ++++++ sam/cloudfront/template.yml | 432 ++++++++++++++++++++++++++++++++++++ sam/standalone/README.md | 9 + sam/standalone/template.yml | 14 ++ 5 files changed, 530 insertions(+), 152 deletions(-) create mode 100644 sam/cloudfront/README.md create mode 100644 sam/cloudfront/template.yml create mode 100644 sam/standalone/README.md create mode 100644 sam/standalone/template.yml diff --git a/README.md b/README.md index e4ee48f..6251734 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Previous versions of this application featured an optional [CloudFront](https://aws.amazon.com/cloudfront/) distribution that provided caching, custom domain/hostname mapping, and request/response pre/post-processing. However, the primary motivation for including this feature in the past was that it provided a complicated but effective way to skirt the hard 6 megabyte limit for Lambda function response payloads. Since then, AWS has introduced [AWS Lambda response streaming](https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/), which uses chunked responses to bypass the 6 megabyte limit. As this is a much more elegant solution to the problem, there's nothing about the CloudFront template that's specific to this project any more. Ongoing development and maintenance will therefore focus on the IIIF Lambda itself rather than the large, complicated template required for a flexible, customizable CloudFront deployment. -While the CloudFront-enabled version of the application is no longer available in the Serverless Application Repository, the newly created [`examples`](./examples/README.md) directory includes sample [CloudFormation](https://aws.amazon.com/cloudformation/) templates and [Terraform](https://terraform.io/) manifests showing how to deploy the IIIF service as part of a larger application/infrastructure stack. +While the CloudFront-enabled version of the application will remain available for the in the Serverless Application Repository for a time to provide an easy upgrade path for existing users, it is _strongly_ recommended that new deployments use the provided [documentation](https://samvera.github.io/serverless-iiif/docs/quick-start/infrastructure) and [examples](./examples) of [CloudFormation](https://aws.amazon.com/cloudformation/) templates and [Terraform](https://terraform.io/) manifests to deploy the standalone function as part of a larger application/infrastructure stack that defines its own CloudFront distribution. ### Breaking Changes from Version 4.x @@ -33,74 +33,17 @@ A IIIF [2.1](https://iiif.io/api/image/2.1/) and [3.0](https://iiif.io/api/image * An [Amazon S3](https://aws.amazon.com/s3/) bucket to hold the source images to be served via IIIF. **Note: The Lambda Function will be granted read access to this bucket.** -## Quick Start +## Getting Started -`serverless-iiif` comes in two flavors: *Standalone (Lambda-only)* and *Caching (CloudFront-enabled)*. The Standalone version is much simpler, but lacks the following features: +For information on deployment, generating source images, advanced features, and customization, see the [full online documentation](https://samvera.github.io/serverless-iiif/docs). -- Custom Domain Name - - Standalone URLs are in the `lambda-url.AWS_REGION.on.aws` domain (e.g., `https://fu90293j0pj902j902c32j902.lambda-url.us-east-1.on.aws/iiif/2/`) - - Caching URLs *without* Custom Domains are in the `cloudfront.net` domain (e.g., `https://d3kmjdzzy1l5t3.cloudfront.net/iiif/2/`) -- Responses larger than ~6MB -- CloudFront function support (for pre/post-processing requests and responses) - -### Deploying via the AWS Serverless Application Repository - -`serverless-iiif` is distributed and deployed via the [AWS Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo/). To deploy it using the AWS Console: - -1. Click one of the following links to deploy the desired application from the AWS Console: - - [Standalone (Lambda-Only) Version](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif-standalone) - - [Caching (CloudFront-Enabled) Version](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif-cloudfront) -2. Make sure your currently selected region (in the console's top navigation bar) is the one you want to deploy to. -3. Scroll down to the **Application settings** section. -4. Configure the deploy template: - - Give your stack a unique **Application name** - - Enter the name of the **SourceBucket** the service will serve images from - - Check the box acknowledging that the app will create a custom IAM roles and resource policies (and if deploying the Caching version, that it will also deploy a nested application) - - *Optional*: Enter or change any other parameters that apply to your desired configuration. -5. Click **Deploy**. -6. When all the resources are properly created and configured, the new stack should be in the **CREATE_COMPLETE** stage. If there's an error, it will delete all the resources it created, roll back any changes it made, and eventually reach the **ROLLBACK_COMPLETE** stage. -7. Click the **CloudFormation stack** link. -8. Click the **Outputs** tab to see (and copy) the IIIF Endpoint URL. - -### Deploying via the Command Line - -1. Make sure you have the [SAM CLI](https://aws.amazon.com/serverless/sam/) and [AWS CLI](https://aws.amazon.com/cli/) installed. -2. Make sure the AWS CLI is [properly configured](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) with credentials that have sufficient access to manage IAM, S3, Lambda, and (optionally) CloudFront resources. -3. Clone this repository. -4. Copy deploy.yml.example to deploy.yml -5. Build the application: - ```shell - $ npm run build - ``` -6. Deploy the application: - ```shell - $ npm run deploy - ``` -7. Follow the prompts to complete the deployment process and get the resulting endpoint. - -If you'd repfer you can run `npm run deploy-guided`. You'll be prompted for various configuration parameters, confirmations, and acknowledgments of specific issues (particularly the creation of IAM resources and the deployment of an open/unauthenticated Lambda Function URL). - -### Deleting the application - -The easiest way to delete the application is either from the [Lambda Applications Console](https://console.aws.amazon.com/lambda/home#/applications) or by deleting its [CloudFormation Stack](https://console.aws.amazon.com/cloudformation/home#/stacks?filteringStatus=active&filteringText=&viewNested=true&hideStacks=false). If you deployed from the command line, you can also use the `npm run delete` command. - -## Source Images - -The S3 key of any given file, minus the extension, is its IIIF ID. For example, if you want to access the image manifest for the file at `abcdef.tif`, you would get `https://.../iiif/2/abcdef/info.json`. If your key contains slashes, they must be URL-encoded: e.g., `ab/cd/ef/gh.tif` would be at `https://.../iiif/2/ab%2Fcd%2Fef%2Fgh/info.json`. (This limitation could easily be fixed by encoding only the necessary slashes in the incoming URL before handing it off to the IIIF processor, but that's beyond the scope of the demo.) - -`iiif-processor` can use any image format _natively_ supported by [libvips](https://libvips.github.io/libvips/), including JPEG 2000 (`.jp2`), but best results will come from using tiled, multi-resolution TIFFs. The Lambda Function wrapper included in this application assumes a `.tif` extension unless you set ResolverTemplate in your .env file. - -### Creating tiled TIFFs - -#### Using VIPS - - vips tiffsave source_image.tif output_image.tif --tile --pyramid --compression jpeg --tile-width 256 --tile-height 256 +## Contributing -#### Using ImageMagick +If you're working on a PR for this project, create a feature branch off of `main`. - convert source_image.tif -define tiff:tile-geometry=256x256 -compress jpeg 'ptif:output_image.tif' +This repository follows the [Samvera Community Code of Conduct](https://samvera.atlassian.net/wiki/spaces/samvera/pages/405212316/Code+of+Conduct) and [language recommendations](https://github.com/samvera/maintenance/blob/main/templates/CONTRIBUTING.md#language). Please ***do not*** create a branch called `master` for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of this repository. -## Testing +### Testing If tests are run locally they will start in "watch" mode. If a CI environment is detected they will only run once. From the project root run: @@ -114,89 +57,6 @@ To generate a code coverage report run: npm test --coverage ``` -## Custom Sharp Layer - -By default, the application uses a [custom sharp layer](https://github.com/samvera/lambda-layer-sharp-jp2/releases) built with JPEG2000 support. You can supply the ARN of another custom sharp/libvips Lambda layer, or, if you don't need JP2 support, use the value `INTERNAL` to use the precompiled sharp binaries from NPM. - -## Advanced Usage - -### Cross-Origin Request Sharing (CORS) - -For security reasons, web browsers have built in limits on what sort of requests can be made to a given domain from a page hosted under a different domain. Since this is a common use case for IIIF (resources embedded in pages whose domains differ from that of the server), IIIF interactions are particularly susceptible to these limits. The mechanism for determining which of these requests should be allowed or blocked is known as Cross-Origin Resource Sharing, or [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). A full explanation of CORS is beyond the scope of this project, but the SAM deploy template contains five parameters relating to how the IIIF server handles CORS: - -* `CorsAllowCredentials` contains the value that will be returned in the `Access-Control-Allow-Credentials` response header. -* `CorsAllowHeaders` contains the value that will be returned in the `Access-Control-Allow-Headers` response header. -* `CorsAllowOrigin` contains the value that will be returned in the `Access-Control-Allow-Origin` response header. In addition, a special value, `REFLECT_ORIGIN`, instructs the IIIF server to copy the value of the incoming request's `Origin` header into the `Access-Control-Allow-Origin` response header. -* `CorsExposeHeaders` contains the value that will be returned in the `Access-Control-Expose-Headers` response header. -* `CorsMaxAge` contains the value that will be returned in the `Access-Control-Max-Age` response header. - -The default values will work in most circumstances, but if you need the IIIF server to accept requests that include credentials or other potentially sensitive information (e.g., `Authorization` and/or `Cookie` headers), you'll need to set `CorsAllowOrigin` to `REFLECT_ORIGIN` and `CorsAllowCredentials` to `true`. Other settings allow further customization. - -### Request/Response Functions - -The SAM deploy template takes several optional parameters to enable the association of [CloudFront Functions](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html) or [Lambda@Edge Functions](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html) with the CloudFront distribution. These functions can perform authentication and authorization functions, change how the S3 file and/or image dimensions are resolved, or alter the response from the lambda or cache. These parameters are: - -* `OriginRequestARN`: ARN of the Lambda@Edge Function to use at the origin-request stage -* `OriginResponseARN`: ARN of the Lambda@Edge Function to use at the origin-response stage -* `ViewerRequestARN`: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-request stage -* `ViewerRequestType`: Type of viewer-request Function to use (`CloudWatch Function` or `Lambda@Edge`) -* `ViewerResponseARN`: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-response stage -* `ViewerResponseType`: Type of viewer-response Function to use (`CloudWatch Function` or `Lambda@Edge`) - -These functions, if used, must be created, configured, and published before the serverless application is deployed. - -#### Examples - -These examples use CloudFront Functions. Lambda@Edge functions are slightly more complicated in terms of the event structure but the basic idea is the same. - -##### Simple Authorization - -```JavaScript -function handler(event) { - if (notAuthorized) { // based on something in the event.request - return { - statusCode: 403, - statusDescription: 'Unauthorized' - }; - }; - return event.request; -} -``` - -##### Custom File Location / Image Dimensions - -```JavaScript -function handler(event) { - var request = event.request; - request.headers['x-preflight-location'] = {value: 's3://image-bucket/path/to/correct/image.tif'}; - request.headers['x-preflight-dimensions'] = {value: JSON.stringify({ width: 640, height: 480 })}; - return request; -} -``` - -The `x-preflight-dimensions` header can take several shapes: - -* `{ width, height }` (or `[{ width, height }]`) - a straightforward, single-resolution image -* `[{ width, height }, { width, height }, ...]` - a multi-resolution image with pages of the specified sizes -* `{ width, height, pages }` - a multi-resolution image with the specified number of `pages`, each half the size of the one before -* `{ width, height, limit }` - a multi-resolution image in which the smallest width and height are both less than the specified `limit` - -For example, the following dimension values would all describe the same pyramidal image: - -* `[{ width: 2048, height: 1536 }, { width: 1024, height: 768 }, { width: 512, height: 384 }]` -* `{ width: 2048, height: 1536, pages: 3 }` -* `{ width: 2048, height: 1536, limit: 480 }` - -The `limit` calculator will keep going until both dimensions are _less than_ the limit, not _less than or equal to_. So a `limit: 512` on the third example above would generate a fourth page at `{ width: 256, height: 192 }`. - -*Note:* The SAM deploy template adds a `preflight=true` environment variable to the main IIIF Lambda if a preflight function is provided. The function will _only_ look for the preflight headers if this environment variable is `true`. This prevents requests from including those headers directly if no preflight function is present. If you do use a preflight function, make sure it strips out any `x-preflight-location` and `x-preflight-dimensions` headers that it doesn't set itself. - -## Notes - -Lambda Function URLs have a payload (request/response body) size limit of approximately 6MB in both directions. To overcome this limitation, the Lambda URL is configured behind an AWS CloudFront distribution with two origins - the API and a cache bucket. Responses larger than 6MB are saved to the cache bucket at the same relative path as the request, and the Lambda returns a `404 Not Found` response to CloudFront. CloudFront then fails over to the second origin (the cache bucket), where it finds the actual response and returns it. - -The cache bucket uses an S3 lifecycle rule to expire cached responses in 1 day. - ## License `serverless-iiif` is available under [the Apache 2.0 license](LICENSE). @@ -210,8 +70,3 @@ The cache bucket uses an S3 lifecycle rule to expire cached responses in 1 day. * [Trey Pendragon](https://github.com/tpendragon) * [Theia Wolfe](https://github.com/theiawolfe) -## Contributing - -If you're working on a PR for this project, create a feature branch off of `main`. - -This repository follows the [Samvera Community Code of Conduct](https://samvera.atlassian.net/wiki/spaces/samvera/pages/405212316/Code+of+Conduct) and [language recommendations](https://github.com/samvera/maintenance/blob/main/templates/CONTRIBUTING.md#language). Please ***do not*** create a branch called `master` for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of this repository. diff --git a/sam/cloudfront/README.md b/sam/cloudfront/README.md new file mode 100644 index 0000000..2761685 --- /dev/null +++ b/sam/cloudfront/README.md @@ -0,0 +1,68 @@ +# serverless-iiif + +## CloudFront Version: Deprecation Notice + +This CloudFront-enabled version of the application was originally written to provide a complicated but effective way to skirt the hard 6 megabyte limit for Lambda function response payloads. Since then, AWS has introduced [AWS Lambda response streaming](https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/), which uses chunked responses to bypass the 6 megabyte limit. As this is a much more elegant solution to the problem, there's nothing about the CloudFront template that's specific to this project any more. Ongoing development and maintenance will therefore focus on the IIIF Lambda itself rather than the large, complicated template required for a flexible, customizable CloudFront deployment. + +The CloudFront-enabled version will remain available for the in the Serverless Application Repository for a time, mostly as a way to upgrade existing CloudFront deployments to v5.0. However, it is _strongly_ recommended that new deployments use the provided [documentation](https://samvera.github.io/serverless-iiif/docs/quick-start/infrastructure) and [examples](https://github.com/samvera/serverless-iiif/tree/main/examples) of [CloudFormation](https://aws.amazon.com/cloudformation/) templates and [Terraform](https://terraform.io/) manifests to deploy the standalone function as part of a larger application/infrastructure stack that defines its own CloudFront distribution. + +### Breaking Changes from Version 4.x + +- The value of the `SharpLayer` variable must now be one of `INTERNAL`, `JP2`, or a valid Lambda layer ARN in the same region the + application is being deployed in. The new default is `JP2`, which behaves the same as the former default (empty string). The new + value, `INTERNAL`, uses the `sharp` and `libvips` dependencies compiled into the application itself. + +## Description + +A IIIF [2.1](https://iiif.io/api/image/2.1/) and [3.0](https://iiif.io/api/image/3.0/) Image API compliant server written as an [AWS Serverless Application](https://aws.amazon.com/serverless/sam/). + +## Components + +* A simple [Lambda Function](https://aws.amazon.com/lambda/) wrapper for the [iiif-processor](https://www.npmjs.com/package/iiif-processor) module. +* A [Lambda Function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html) that is used to invoke the IIIF API via HTTPS. +* A [Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) containing all the dependencies for the Lambda Function. + +## Prerequisites + +* Some basic knowledge of AWS. +* An Amazon Web Services account with permissions to create resources via the console and/or command line. +* An [Amazon S3](https://aws.amazon.com/s3/) bucket to hold the source images to be served via IIIF. + **Note: The Lambda Function will be granted read access to this bucket.** + +## Quick Start via the AWS Serverless Application Repository + +1. Click one of the following links to deploy the desired application from the AWS Console: + - [Standalone (Lambda-Only) Version](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif-standalone) + - [Caching (CloudFront-Enabled) Version](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif-cloudfront) +2. Make sure your currently selected region (in the console's top navigation bar) is the one you want to deploy to. +3. Scroll down to the **Application settings** section. +4. Configure the deploy template: + - Give your stack a unique **Application name** + - Enter the name of the **SourceBucket** the service will serve images from + - Check the box acknowledging that the app will create a custom IAM roles and resource policies (and if deploying the Caching version, that it will also deploy a nested application) + - *Optional*: Enter or change any other parameters that apply to your desired configuration. +5. Click **Deploy**. +6. When all the resources are properly created and configured, the new stack should be in the **CREATE_COMPLETE** stage. If there's an error, it will delete all the resources it created, roll back any changes it made, and eventually reach the **ROLLBACK_COMPLETE** stage. +7. Click the **CloudFormation stack** link. +8. Click the **Outputs** tab to see (and copy) the IIIF Endpoint URL. + +## Deleting the application + +The easiest way to delete the application is either from the [Lambda Applications Console](https://console.aws.amazon.com/lambda/home#/applications) or by deleting its [CloudFormation Stack](https://console.aws.amazon.com/cloudformation/home#/stacks?filteringStatus=active&filteringText=&viewNested=true&hideStacks=false). + +## Additional Documentation and Source Code + +For information on generating source images for use with `serverless-iiif` as well as advanced features, customizations, and how to contribute to the project, please refer to the [full online documentation](https://samvera.github.io/serverless-iiif) and the [GitHub repository](https://github.com/samvera/serverless-iiif). + +## License + +`serverless-iiif` is available under [the Apache 2.0 license](LICENSE). + +## Contributors + +* [Michael B. Klein](https://github.com/mbklein) +* [Justin Gondron](https://github.com/jgondron) +* [Rob Kaufman](https://github.com/orangewolf) +* [Edward Silverton](https://github.com/edsilv) +* [Trey Pendragon](https://github.com/tpendragon) +* [Theia Wolfe](https://github.com/theiawolfe) diff --git a/sam/cloudfront/template.yml b/sam/cloudfront/template.yml new file mode 100644 index 0000000..fa29377 --- /dev/null +++ b/sam/cloudfront/template.yml @@ -0,0 +1,432 @@ +Transform: "AWS::Serverless-2016-10-31" +Metadata: + AWS::ServerlessRepo::Application: + Name: serverless-iiif-cloudfront + Description: IIIF Image API 2.1/3.0 server in an AWS Serverless Application (w/CloudFront Caching) + Author: Samvera + SpdxLicenseId: Apache-2.0 + LicenseUrl: ../../LICENSE.txt + ReadmeUrl: ./README.md + Labels: ["iiif", "image-processing"] + HomePageUrl: https://samvera.github.io/serverless-iiif + SemanticVersion: 5.0.0 + SourceCodeUrl: https://github.com/samvera/serverless-iiif + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "General Configuration" + Parameters: + - SourceBucket + - IiifLambdaMemory + - IiifLambdaTimeout + - PixelDensity + - ResolverTemplate + - SharpLayer + - Label: + default: "Cache Configuration" + Parameters: + - CachePolicyID + - CachePriceClass + - Label: + default: "CORS Configuration" + Parameters: + - CorsAllowCredentials + - CorsAllowHeaders + - CorsAllowOrigin + - CorsExposeHeaders + - CorsMaxAge + - Label: + default: "Hostname Configuration" + Parameters: + - CacheDomainName + - CacheSSLCertificate + - Label: + default: "Function Configuration" + Parameters: + - OriginRequestARN + - OriginResponseARN + - ViewerRequestARN + - ViewerRequestType + - ViewerResponseARN + - ViewerResponseType +Parameters: + CachePolicyID: + Type: String + Description: The ID of a managed or custom CloudFront Cache Policy to use + Default: 658327ea-f89d-4fab-a63d-7e88639e58f6 + CachePriceClass: + Type: String + Description: Price Class for the CloudFront Cache + Default: PriceClass_100 + AllowedValues: + - PriceClass_100 + - PriceClass_200 + - PriceClass_All + CacheDomainName: + Type: String + Description: Custom Domain Name for the API Gateway Endpoint or CloudFront Cache + Default: "" + CacheSSLCertificate: + Type: String + Description: ARN of the ACM SSL Certification to use for the API Gateway Endpoint or CloudFront Cache + Default: "" + CorsAllowCredentials: + Type: String + Description: > + Value of the CORS `Access-Control-Allow-Credentials` response header. + Must be `true` to allow requests with `Authorization` and/or + `Cookie` headers. + AllowedValues: + - false + - true + Default: false + CorsAllowHeaders: + Type: String + Description: Value of the CORS `Access-Control-Allow-Headers` response header + Default: "*" + CorsAllowOrigin: + Type: String + Description: > + Value of the CORS `Access-Control-Allow-Origin` response header. + Use the special value `REFLECT_ORIGIN` to copy the value from the + `Origin` request header (required to emulate `*` for XHR requests + using `Authorization` and/or `Cookie` headers). + Default: "*" + CorsExposeHeaders: + Type: String + Description: Value of the CORS `Access-Control-Expose-Headers` response header + Default: cache-control,content-language,content-length,content-type,date,expires,last-modified,pragma + CorsMaxAge: + Type: Number + Description: Value of the CORS `Access-Control-MaxAge` response header + Default: 3600 + IiifLambdaMemory: + Type: Number + Description: The memory provisioned for the lambda. + MinValue: 128 + MaxValue: 10240 + Default: 3008 + IiifLambdaTimeout: + Type: Number + Description: The timeout for the lambda. + Default: 10 + OriginRequestARN: + Type: String + Description: ARN of the Lambda@Edge Function to use at the origin-request stage + Default: "" + OriginResponseARN: + Type: String + Description: ARN of the Lambda@Edge Function to use at the origin-response stage + Default: "" + PixelDensity: + Type: Number + Description: Hardcoded DPI/Pixel Density/Resolution to encode in output images + Default: 0 + MinValue: 0 + PyramidLimit: + Type: Number + Description: Smallest pyramid image dimension. Set to `0` to prevent server from auto-calculating pyramid page sizes. + MinValue: 0 + Default: 256 + ResolverTemplate: + Type: String + Description: A printf-style format string that determines the location of source image within the bucket given the image ID + Default: "%s.tif" + SharpLayer: + Type: String + Description: > + ARN of a custom AWS Lambda Layer containing the sharp and libvips dependencies. Use the special value `JP2` + to use the managed JPEG2000-compatible layer, or `INTERNAL` to use the built-in dependencies (without JPEG2000 + support). + AllowedPattern: "^INTERNAL$|^JP2$|^arn:aws:lambda:.*:.*:layer:.+:\\d+$" + Default: "JP2" + SourceBucket: + Type: String + Description: Name of bucket containing source images + ViewerRequestARN: + Type: String + Description: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-request stage + Default: "" + ViewerRequestType: + Type: String + Description: Type of viewer-request Function to use (CloudWatch Function or Lambda@Edge) + Default: "None" + AllowedValues: + - CloudWatch Function + - Lambda@Edge + - None + ViewerResponseARN: + Type: String + Description: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-response stage + Default: "" + ViewerResponseType: + Type: String + Description: Type of viewer-response Function to use (CloudWatch Function or Lambda@Edge) + Default: "None" + AllowedValues: + - CloudWatch Function + - Lambda@Edge + - None +Conditions: + DistributionCustomDomain: + Fn::And: + - Fn::Not: + - Fn::Equals: [!Ref CacheDomainName, ""] + - Fn::Not: + - Fn::Equals: [!Ref CacheSSLCertificate, ""] + UseOriginRequest: + Fn::Not: + - Fn::Equals: [!Ref OriginRequestARN, ""] + UseOriginResponse: + Fn::Not: + - Fn::Equals: [!Ref OriginResponseARN, ""] + UseViewerRequest: + Fn::And: + - Fn::Not: + - Fn::Equals: [!Ref ViewerRequestType, "None"] + - Fn::Not: + - Fn::Equals: [!Ref ViewerRequestARN, ""] + UseViewerResponse: + Fn::And: + - Fn::Not: + - Fn::Equals: [!Ref ViewerResponseType, "None"] + - Fn::Not: + - Fn::Equals: [!Ref ViewerResponseARN, ""] + ViewerRequestCloudWatchFunction: + Fn::And: + - Condition: UseViewerRequest + - Fn::Equals: [!Ref ViewerRequestType, "CloudWatch Function"] + ViewerRequestLambda: + Fn::And: + - Condition: UseViewerRequest + - Fn::Equals: [!Ref ViewerRequestType, "Lambda@Edge"] + ViewerResponseCloudWatchFunction: + Fn::And: + - Condition: UseViewerResponse + - Fn::Equals: [!Ref ViewerResponseType, "CloudWatch Function"] + ViewerResponseLambda: + Fn::And: + - Condition: UseViewerResponse + - Fn::Equals: [!Ref ViewerResponseType, "Lambda@Edge"] +Resources: + CacheBucket: + Type: "AWS::S3::Bucket" + Properties: + BucketName: + Fn::Sub: "${AWS::StackName}-cache" + LifecycleConfiguration: + Rules: + - Status: Enabled + ExpirationInDays: 1 + PublicAccessBlockConfiguration: + BlockPublicAcls : true + BlockPublicPolicy : true + IgnorePublicAcls : true + RestrictPublicBuckets : true + CacheBucketPolicy: + Type: "AWS::S3::BucketPolicy" + Properties: + Bucket: !Ref CacheBucket + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - s3:GetObject + Resource: + - Fn::Sub: "arn:aws:s3:::${CacheBucket}/*" + Principal: + CanonicalUser: + Fn::GetAtt: CachingIdentity.S3CanonicalUserId + CachingIdentity: + Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity" + Properties: + CloudFrontOriginAccessIdentityConfig: + Comment: "Caching Distribution Identity" + OriginRequestPolicy: + Type: "AWS::CloudFront::OriginRequestPolicy" + Properties: + OriginRequestPolicyConfig: + Name: !Sub "${AWS::StackName}-allow-preflight-headers" + Comment: Allows IIIF preflight headers + CookiesConfig: + CookieBehavior: none + HeadersConfig: + HeaderBehavior: whitelist + Headers: + - x-preflight-location + - x-preflight-dimensions + QueryStringsConfig: + QueryStringBehavior: none + ResponseHeaderPolicy: + Type: "AWS::CloudFront::ResponseHeadersPolicy" + Properties: + ResponseHeadersPolicyConfig: + Name: !Sub "${AWS::StackName}-allow-cors-response-headers" + Comment: Allows IIIF CORS response headers + CorsConfig: + AccessControlAllowCredentials: false + AccessControlAllowHeaders: + Items: ["*"] + AccessControlAllowMethods: + Items: ["GET", "OPTIONS"] + AccessControlAllowOrigins: + Items: ["*"] + AccessControlExposeHeaders: + Items: ["cache-control", "content-language", "content-length", "content-type", "date", "expires", "last-modified", "pragma"] + AccessControlMaxAgeSec: 3600 + OriginOverride: false + CachingEndpoint: + Type: "AWS::CloudFront::Distribution" + Properties: + DistributionConfig: + Enabled: true + PriceClass: !Ref CachePriceClass + Aliases: + Fn::If: + - DistributionCustomDomain + - !Split [",", !Ref CacheDomainName] + - !Ref AWS::NoValue + ViewerCertificate: + Fn::If: + - DistributionCustomDomain + - AcmCertificateArn: !Ref CacheSSLCertificate + MinimumProtocolVersion: 'TLSv1' + SslSupportMethod: 'sni-only' + - CloudFrontDefaultCertificate: true + Origins: + - Id: IiifLambda + CustomOriginConfig: + OriginProtocolPolicy: https-only + DomainName: + Fn::GetAtt: IiifFunction.Outputs.FunctionDomain + - Id: IiifCache + S3OriginConfig: + OriginAccessIdentity: + Fn::Join: + - '' + - - 'origin-access-identity/cloudfront/' + - !Ref CachingIdentity + DomainName: + Fn::Sub: "${CacheBucket}.s3.${AWS::Region}.amazonaws.com" + OriginGroups: + Quantity: 1 + Items: + - Id: IiifOrigins + Members: + Quantity: 2 + Items: + - OriginId: IiifLambda + - OriginId: IiifCache + FailoverCriteria: + StatusCodes: + Items: [404] + Quantity: 1 + DefaultCacheBehavior: + TargetOriginId: IiifOrigins + ViewerProtocolPolicy: https-only + AllowedMethods: ["GET", "HEAD", "OPTIONS"] + CachedMethods: ["GET", "HEAD"] + CachePolicyId: !Ref CachePolicyID + OriginRequestPolicyId: !Ref OriginRequestPolicy + ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy + FunctionAssociations: + - Fn::If: + - ViewerRequestCloudWatchFunction + - EventType: viewer-request + FunctionARN: !Ref ViewerRequestARN + - !Ref AWS::NoValue + - Fn::If: + - ViewerResponseCloudWatchFunction + - EventType: viewer-response + FunctionARN: !Ref ViewerResponseARN + - !Ref AWS::NoValue + LambdaFunctionAssociations: + - Fn::If: + - ViewerRequestLambda + - EventType: viewer-request + LambdaFunctionARN: !Ref ViewerRequestARN + IncludeBody: false + - !Ref AWS::NoValue + - Fn::If: + - UseOriginRequest + - EventType: origin-request + LambdaFunctionARN: !Ref OriginRequestARN + IncludeBody: false + - !Ref AWS::NoValue + - Fn::If: + - UseOriginResponse + - EventType: origin-response + LambdaFunctionARN: !Ref OriginResponseARN + IncludeBody: false + - !Ref AWS::NoValue + - Fn::If: + - ViewerResponseLambda + - EventType: viewer-response + LambdaFunctionARN: !Ref ViewerResponseARN + IncludeBody: false + - !Ref AWS::NoValue + IiifFunction: + Type: AWS::Serverless::Application + Properties: + # Swap the comment characters on the two Location properties and update SemanticVersion + # when publishing to SAR + Location: + ApplicationId: arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif + SemanticVersion: 5.0.0 + Parameters: + CacheBucket: !Ref CacheBucket + CorsAllowCredentials: !Ref CorsAllowCredentials + CorsAllowOrigin: !Ref CorsAllowOrigin + CorsAllowHeaders: !Ref CorsAllowHeaders + CorsExposeHeaders: !Ref CorsExposeHeaders + CorsMaxAge: !Ref CorsMaxAge + ForceHost: + Fn::If: + - DistributionCustomDomain + - !Select [0, !Split [",", !Ref CacheDomainName]] + - !Ref AWS::NoValue + IiifLambdaMemory: !Ref IiifLambdaMemory + IiifLambdaTimeout: !Ref IiifLambdaTimeout + PixelDensity: !Ref PixelDensity + Preflight: + Fn::If: + - UseViewerRequest + - true + - false + PyramidLimit: !Ref PyramidLimit + ResolverTemplate: !Ref ResolverTemplate + SharpLayer: !Ref SharpLayer + SourceBucket: !Ref SourceBucket +Outputs: + EndpointV2: + Description: IIIF Endpoint URL + Value: + Fn::Join: + - "" + - - "https://" + - Fn::If: + - DistributionCustomDomain + - !Select [0, !Split [",", !Ref CacheDomainName]] + - !GetAtt CachingEndpoint.DomainName + - "/iiif/2" + EndpointV3: + Description: IIIF Endpoint URL + Value: + Fn::Join: + - "" + - - "https://" + - Fn::If: + - DistributionCustomDomain + - !Select [0, !Split [",", !Ref CacheDomainName]] + - !GetAtt CachingEndpoint.DomainName + - "/iiif/3" + DistributionId: + Description: Caching Distribution ID + Value: + Ref: CachingEndpoint + Export: + Name: !Sub "${AWS::StackName}:DistributionId" + LambdaFunction: + Description: IIIF Lambda Function Name + Value: !Ref IiifFunction diff --git a/sam/standalone/README.md b/sam/standalone/README.md new file mode 100644 index 0000000..1d791fc --- /dev/null +++ b/sam/standalone/README.md @@ -0,0 +1,9 @@ +# serverless-iiif-standalone + +**Note: This application has been republished simply as [`serverless-iiif`](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif). Please use that link for deployment.** + +## Standalone Version: Deprecation Notice + +This standalone version of the application was originally created to distinguish it from the CloudFront-enabled version, which was written to provide a complicated but effective way to skirt the hard 6 megabyte limit for Lambda function response payloads. Since then, AWS has introduced [AWS Lambda response streaming](https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/), which uses chunked responses to bypass the 6 megabyte limit. As this is a much more elegant solution to the problem, there's nothing about the CloudFront template that's specific to this project any more. Ongoing development and maintenance will therefore focus on the IIIF Lambda itself rather than the large, complicated template required for a flexible, customizable CloudFront deployment. + +With the deprecation of the CloudFront version, we have removed the `-standalone` suffix from the core application. The application is therefore published simply as [`serverless-iiif`](https://console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:625046682746:applications/serverless-iiif). Please see the [documentation](https://samvera.github.io/serverless-iiif/docs/quick-start/infrastructure) and [examples](https://github.com/samvera/serverless-iiif/tree/main/examples) of [CloudFormation](https://aws.amazon.com/cloudformation/) templates and [Terraform](https://terraform.io/) manifests to deploy the standalone function as part of a larger application/infrastructure stack. diff --git a/sam/standalone/template.yml b/sam/standalone/template.yml new file mode 100644 index 0000000..bd12c7a --- /dev/null +++ b/sam/standalone/template.yml @@ -0,0 +1,14 @@ +Transform: "AWS::Serverless-2016-10-31" +Metadata: + AWS::ServerlessRepo::Application: + Name: serverless-iiif-standalone + Description: IIIF Image API 2.1/3.0 server backend Lambda function + Author: Samvera + SpdxLicenseId: Apache-2.0 + LicenseUrl: ../../LICENSE.txt + ReadmeUrl: ./README.md + Labels: ["iiif", "image-processing"] + HomePageUrl: https://samvera.github.io/serverless-iiif + SemanticVersion: 5.0.0 + SourceCodeUrl: https://github.com/samvera/serverless-iiif +Resources: {}