Giter Site home page Giter Site logo

pleo-io / pleo-spa-infra Goto Github PK

View Code? Open in Web Editor NEW
0.0 3.0 3.0 234 KB

Reusable infrastructure components for Single Page Applications

License: MIT License

Makefile 0.19% TypeScript 23.27% HCL 10.87% HTML 65.68%
terraform-module lambda-edge aws-s3 aws-cloudfront single-page-app

pleo-spa-infra's Introduction

This repository has been archived, please use spa-tools instead.

๐Ÿ”‹ Pleo SPA Infrastructure

๐Ÿ’ก A Terraform Module which creates a complete serving infrastructure for a Single Page Application on AWS.

๐Ÿ‘จโ€๐Ÿ”ง This repo is lovingly stewarded by Pleo's Web Core team.

๐Ÿ› Issues should be reported in the repo or via Stewards: Pleo SPA project on Linear (if you have access).

Contents

Contributing

This repo uses Semantic Release to version the changes and keep an up-to-date changelog file. When creating a PR, make sure that the squash commit title follows the semantic commit standards.

pleo-spa-infra's People

Contributors

andersfischernielsen avatar igordolzh avatar jsfr avatar pekala avatar semantic-release-bot avatar skovhus avatar tkloht avatar

Watchers

 avatar  avatar  avatar

Forkers

mik-patient

pleo-spa-infra's Issues

Issues with edge lambda deployment using Terraform

Context

Lambda@Edge versioned in this repo under edge-lambdas directory are deployed to AWS using Terraform, when the Terraform module version in this repo under terraform-module is used.

Workflow

In theory, the workflow to deploy a new version of the lambda goes like this:

  1. Make a change to the lambda code
  2. Build the lambda locally and commit source AND the build output (in the edge-lambdas/dist directory) in the repo
  3. Update the version of the terraform module in terraform configuration (could be after a new release of this repo is tagged, or using SHA of the commit to test unreleased version)
  4. Internally, the terraform module copies the built lambda file from the repo to a local file in a dist directory inside the TF component directory
  5. Terraform modules creates JSON configuration files that are deployed together with the lambas and places them in the same dist directory
  6. Terraform modules bundles the JS and JSON files from the dist directory into archive files that can be used as source for the lambda resources
  7. Terraform updates or creates the lambda resource with the new source using the archive file
  8. Lambdas are the used to associate with a behaviour of the Cloudfront distribution

Problem

In theory this works well. A terraform plan to update a lambda version would look like this:

See tf plan
  # module.app.module.cdn.aws_cloudfront_distribution.this will be updated in-place
  ~ resource "aws_cloudfront_distribution" "this" {
        id                             = "<id-removed>"
        tags                           = {
            "Environment" = "staging"
        }
        # (17 unchanged attributes hidden)


      ~ default_cache_behavior {
            # (10 unchanged attributes hidden)


          - lambda_function_association {
              - event_type   = "viewer-request" -> null
              - include_body = false -> null
              - lambda_arn   = "<arn-removed>:function:product-web-viewer-request:10" -> null
            }
          + lambda_function_association {
              + event_type   = "viewer-request"
              + include_body = false
              + lambda_arn   = (known after apply)
            }
          - lambda_function_association {
              - event_type   = "viewer-response" -> null
              - include_body = false -> null
              - lambda_arn   = "<arn-removed>:function:product-web-viewer-response:14" -> null
            }
          + lambda_function_association {
              + event_type   = "viewer-response"
              + include_body = false
              + lambda_arn   = (known after apply)
            }
            # (1 unchanged block hidden)
        }




        # (5 unchanged blocks hidden)
    }

  # module.app.module.lambdas["viewer-request"].data.archive_file.lambda will be read during apply
  # (config refers to values not yet known)
 <= data "archive_file" "lambda"  {
      ~ id                  = "<id-removed>" -> (known after apply)
      ~ output_base64sha256 = "fmB/SIWINKRMecElQj/DYyvAAZBBZBEhAbeeYbJE/Vg=" -> (known after apply)
      ~ output_md5          = "9053640da322f129121a0dc31e511e8a" -> (known after apply)
      ~ output_sha          = "4cc6563dbb38e835b567ae64996a93073751a8c5" -> (known after apply)
      ~ output_size         = 4142 -> (known after apply)
        # (3 unchanged attributes hidden)
    }

  # module.app.module.lambdas["viewer-request"].aws_lambda_function.lambda will be updated in-place
  ~ resource "aws_lambda_function" "lambda" {
        id                             = "product-web-viewer-request"
      ~ last_modified                  = "2022-05-27T10:26:04.000+0000" -> (known after apply)
      ~ qualified_arn                  = "<arn-removed>:function:product-web-viewer-request:10" -> (known after apply)
      ~ source_code_hash               = "bXN1a32UZ9zYM6GB+63wxwe206uWavGxyRPyC5IjzN8=" -> (known after apply)
        tags                           = {
            "environment" = "staging"
        }
      ~ version                        = "10" -> (known after apply)
        # (14 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.app.module.lambdas["viewer-request"].local_file.lambda_config will be created
  + resource "local_file" "lambda_config" {
      + content              = jsonencode(
            {
              + blockIframes             = "true"
              + blockRobots              = "true"
              + defaultBranchName        = "master"
              + originBucketName         = "<bucket-name-removed>"
              + originBucketRegion       = "eu-west-1"
              + previewDeploymentPostfix = ".app.staging.pleo.io"
            }
        )
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./dist/product-web/viewer-request/config.json"
      + id                   = (known after apply)
    }

  # module.app.module.lambdas["viewer-request"].local_file.lambda_source will be created
  + resource "local_file" "lambda_source" {
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./dist/product-web/viewer-request/index.js"
      + id                   = (known after apply)
      + sensitive_content    = (sensitive value)
    }

  # module.app.module.lambdas["viewer-response"].data.archive_file.lambda will be read during apply
  # (config refers to values not yet known)
 <= data "archive_file" "lambda"  {
      ~ id                  = "<id-removed>" -> (known after apply)
      ~ output_base64sha256 = "2Jc8TNyZBFxo6mO1PVrXuJzjq+iZ7uUNUkqEGY1jztE=" -> (known after apply)
      ~ output_md5          = "3857ff4308a37810aafec9ee69297792" -> (known after apply)
      ~ output_sha          = "a6be7e2cdb419c3052de4f64584908012c78f7e9" -> (known after apply)
      ~ output_size         = 3768 -> (known after apply)
        # (3 unchanged attributes hidden)
    }

  # module.app.module.lambdas["viewer-response"].aws_lambda_function.lambda will be updated in-place
  ~ resource "aws_lambda_function" "lambda" {
        id                             = "product-web-viewer-response"
      ~ last_modified                  = "2022-06-02T14:42:43.000+0000" -> (known after apply)
      ~ qualified_arn                  = "<arn-removed>:function:product-web-viewer-response:14" -> (known after apply)
      ~ source_code_hash               = "/bijzg8RcLYDsmFv4/1X9bVEC/rMBVqeZy/ZklE/Adc=" -> (known after apply)
        tags                           = {
            "environment" = "staging"
        }
      ~ version                        = "14" -> (known after apply)
        # (14 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.app.module.lambdas["viewer-response"].local_file.lambda_config will be created
  + resource "local_file" "lambda_config" {
      + content              = jsonencode(
            {
              + blockIframes             = "true"
              + blockRobots              = "true"
              + defaultBranchName        = "master"
              + originBucketName         = "<bucket-name-removed>"
              + originBucketRegion       = "eu-west-1"
              + previewDeploymentPostfix = ".app.staging.pleo.io"
            }
        )
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./dist/product-web/viewer-response/config.json"
      + id                   = (known after apply)
    }

  # module.app.module.lambdas["viewer-response"].local_file.lambda_source will be created
  + resource "local_file" "lambda_source" {
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./dist/product-web/viewer-response/index.js"
      + id                   = (known after apply)
      + sensitive_content    = (sensitive value)
    }

However, most of the time (not always) this will result in an error:

โ”‚ Error: Error publishing Lambda Function Version product-web-viewer-request: ResourceConflictException: The operation cannot be performed at this time. An update is in progress for resource: <arn-removed>:function:product-web-viewer-request
โ”‚ {
โ”‚   RespMetadata: {
โ”‚     StatusCode: 409,
โ”‚     RequestID: "61bcc7d6-bcca-4c33-b7d6-9de27be98abd"
โ”‚   },
โ”‚   Message_: "The operation cannot be performed at this time. An update is in progress for resource: <arn-removed>:function:product-web-viewer-request",
โ”‚   Type: "User"
โ”‚ }
โ”‚ 
โ”‚   with module.app.module.lambdas["viewer-request"].aws_lambda_function.lambda,
โ”‚   on .terraform/modules/app/terraform-module/modules/frontend-spa-edge-lambda/main.tf line 28, in resource "aws_lambda_function" "lambda":
โ”‚   28: resource "aws_lambda_function" "lambda" {
โ”‚ 
โ•ต
โ•ท
โ”‚ Error: Error publishing Lambda Function Version product-web-viewer-response: ResourceConflictException: The operation cannot be performed at this time. An update is in progress for resource: <arn-removed>:function:product-web-viewer-response
โ”‚ {
โ”‚   RespMetadata: {
โ”‚     StatusCode: 409,
โ”‚     RequestID: "f3780c01-190e-4a04-8ae8-000757e85c67"
โ”‚   },
โ”‚   Message_: "The operation cannot be performed at this time. An update is in progress for resource: <arn-removed>:function:product-web-viewer-response",
โ”‚   Type: "User"
โ”‚ }
โ”‚ 
โ”‚   with module.app.module.lambdas["viewer-response"].aws_lambda_function.lambda,
โ”‚   on .terraform/modules/app/terraform-module/modules/frontend-spa-edge-lambda/main.tf line 28, in resource "aws_lambda_function" "lambda":
โ”‚   28: resource "aws_lambda_function" "lambda" {
โ”‚ 
โ•ต

When I run the apply again:

No changes. Your infrastructure matches the configuration.

In AWS console, I can see the code for the lambda was uploaded, but a new version was not published, and so Cloudfront distribution was not updated with the new version. Manually publishing the version and updating the behaviour version is needed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.