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

AWS asset duplication #27

Open
hecarrillo opened this issue Aug 31, 2024 · 0 comments
Open

AWS asset duplication #27

hecarrillo opened this issue Aug 31, 2024 · 0 comments

Comments

@hecarrillo
Copy link

I have a setup based on this template. However, it seems that the setup only works if I create the function construct and also the docker version of the same function, resulting in the same function being deployed twice to AWS. Which would be the appropriate way of handling a multi-function setup given that I only use the docker setup?

The setup I currently use looks like this:

import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as apigateway from "aws-cdk-lib/aws-apigateway";
import * as iam from "aws-cdk-lib/aws-iam";
import { DatabaseConnectionProps, PrismaFunction } from "./prisma-function";
import { Construct } from "constructs";
import { DockerPrismaFunction } from "./docker-prisma-function";
import { DockerImageCode, Function, Runtime } from "aws-cdk-lib/aws-lambda";
import { Platform } from "aws-cdk-lib/aws-ecr-assets";
import { InvocationType, Trigger } from "aws-cdk-lib/triggers";
import { UserPool, UserPoolClient } from "aws-cdk-lib/aws-cognito";
import { ResponseHeadersPolicy } from "aws-cdk-lib/aws-cloudfront";
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';

interface ApplicationProps {
  vpc: ec2.IVpc;
  database: DatabaseConnectionProps;
  userPool: UserPool;
  userPoolClient: UserPoolClient;
  blogPostBucket: s3.IBucket;
  cloudfrontDistribution: cloudfront.IDistribution;
}

export class Application extends Construct {
  readonly lambdaSecurityGroup: ec2.ISecurityGroup;
  readonly migrationHandler: Function;
  readonly userAPI: apigateway.LambdaRestApi;

  constructor(scope: Construct, id: string, props: ApplicationProps) {
    super(scope, id);

    const { vpc, database, userPool, userPoolClient } = props;

    const securityGroup = new ec2.SecurityGroup(this, `SecurityGroup`, {
      vpc: props.vpc,
    });

    // Create a Cognito User Pool Authorizer
    const authorizer = new apigateway.CognitoUserPoolsAuthorizer(this, 'CognitoAuthorizer', {
      cognitoUserPools: [props.userPool]
    });

    // Create an API Gateway with API Key authentication
    const api = new apigateway.RestApi(this, 'ra-api', {
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS, 
        allowHeaders: apigateway.Cors.DEFAULT_HEADERS.concat(['x-api-key', 'Authorization'])
      }
    });

    const migrationRunner = new PrismaFunction(this, "MigrationRunner", {
      entry: "./app/src/migration/migration-runner.ts",
      memorySize: 256,
      runtime: Runtime.NODEJS_20_X,
      timeout: cdk.Duration.minutes(1),
      vpc,
      securityGroups: [securityGroup],
      database,
      depsLockFilePath: "./app/package-lock.json",
    });

    new DockerPrismaFunction(this, "DockerMigrationRunner", {
      code: DockerImageCode.fromImageAsset("./app", {
        cmd: ["migration-runner.handler"],
        platform: Platform.LINUX_ARM64,
      }),
      memorySize: 256,
      timeout: cdk.Duration.minutes(1),
      vpc,
      securityGroups: [securityGroup],
      database,
    });

    const generatePresignedUrlHandler = new PrismaFunction(this, "GeneratePresignedUrlHandler", {
      entry: "./app/src/img/generatePresignedUrl.ts",
      memorySize: 256,
      runtime: Runtime.NODEJS_20_X,
      timeout: cdk.Duration.minutes(1),
      vpc,
      securityGroups: [securityGroup],
      database,
      depsLockFilePath: "./app/package-lock.json",
      environment: {
        BUCKET_NAME: props.blogPostBucket.bucketName,
        CLOUDFRONT_DOMAIN: props.cloudfrontDistribution.distributionDomainName,
      },
    });

    new DockerPrismaFunction(this, "DockerGeneratePresignedUrlHandler", {
      code: DockerImageCode.fromImageAsset("./app", {
        cmd: ["generatePresignedUrl.handler"],
        platform: Platform.LINUX_ARM64,
      }),
      memorySize: 256,
      timeout: cdk.Duration.minutes(1),
      vpc,
      securityGroups: [securityGroup],
      database,
      environment: {
        BUCKET_NAME: props.blogPostBucket.bucketName,
        CLOUDFRONT_DOMAIN: props.cloudfrontDistribution.distributionDomainName,
      },
    });

    props.blogPostBucket.grantReadWrite(generatePresignedUrlHandler);

    // img/presigned-url
    const img = api.root.addResource('img');
    const presignedUrl = img.addResource('presigned-url');

    presignedUrl.addMethod('GET', new apigateway.LambdaIntegration(generatePresignedUrlHandler), {
      authorizer,
      authorizationType: apigateway.AuthorizationType.COGNITO,
    });

    const userHandler = new PrismaFunction(this, "User", {
      entry: "./app/src/user/userIndex.ts", 
      memorySize: 256,
      runtime: Runtime.NODEJS_20_X,
      timeout: cdk.Duration.seconds(30),
      vpc,
      securityGroups: [securityGroup],
      database,
      depsLockFilePath: "./app/package-lock.json",
      environment: {
        COGNITO_USER_POOL_ID: userPool.userPoolId,
        COGNITO_USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
      },
    });

    new DockerPrismaFunction(this, "UserHandler", {
      code: DockerImageCode.fromImageAsset("./app", {
        cmd: ["userIndex.handler"],
        platform: Platform.LINUX_ARM64,
      }),
      memorySize: 256,
      timeout: cdk.Duration.seconds(30),
      vpc,
      securityGroups: [securityGroup],
      database,
      environment: {
        COGNITO_USER_POOL_ID: userPool.userPoolId,
        COGNITO_USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
      },
    });

    userHandler.addToRolePolicy(new iam.PolicyStatement({
      actions: [
        "cognito-idp:AdminCreateUser",
        "cognito-idp:AdminUpdateUserAttributes",
        "cognito-idp:AdminGetUser",
      ],
      resources: [props.userPool.userPoolArn],
    }));
    
    const user = api.root.addResource('user');

    const user_me = user.addResource('me');
    user_me.addMethod('GET', new apigateway.LambdaIntegration(userHandler), {
      authorizer,
      authorizationType: apigateway.AuthorizationType.COGNITO, 
    });
    user.addMethod('PUT', new apigateway.LambdaIntegration(userHandler), {
      apiKeyRequired: false,
    });
    user.addMethod('POST', new apigateway.LambdaIntegration(userHandler), {
      authorizer,
      authorizationType: apigateway.AuthorizationType.COGNITO,
    });

    const blogHandler = new PrismaFunction(this, "BlogHandler", {
      entry: "./app/src/blog/blogIndex.ts",
      memorySize: 256,
      runtime: Runtime.NODEJS_20_X,
      timeout: cdk.Duration.seconds(30),
      vpc,
      securityGroups: [securityGroup],
      database,
      depsLockFilePath: "./app/package-lock.json",
      environment: {
        COGNITO_USER_POOL_ID: userPool.userPoolId,
        COGNITO_USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
      },
    });
    
    // Add Docker version of the blog handler
    new DockerPrismaFunction(this, "DockerBlogHandler", {
      code: DockerImageCode.fromImageAsset("./app", {
        cmd: ["blogIndex.handler"],
        platform: Platform.LINUX_ARM64,
      }),
      memorySize: 256,
      timeout: cdk.Duration.seconds(30),
      vpc,
      securityGroups: [securityGroup],
      database,
      environment: {
        COGNITO_USER_POOL_ID: userPool.userPoolId,
        COGNITO_USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
      },
    });
    
    // Add blog resource to API
    const blog = api.root.addResource('blog');
    
    // Add POST method for blog creation
    blog.addMethod('POST', new apigateway.LambdaIntegration(blogHandler), {
      authorizer,
      authorizationType: apigateway.AuthorizationType.COGNITO,
    });

    const plan = api.addUsagePlan('UsagePlan', {
      name: 'StandardUsagePlan',
      throttle: {
        rateLimit: 10,
        burstLimit: 10
      },
    });

    const apiKey = api.addApiKey('UsersApiKey');
    plan.addApiKey(apiKey);

    new cdk.CfnOutput(this, `MigrationRunnerLambdaArn`, { value: migrationRunner.functionArn });
    new cdk.CfnOutput(this, `UserApiUrl`, { value: api.url });
    new cdk.CfnOutput(this, `UserApiKey`, { 
      value: apiKey.keyId,
      description: 'API Key ID for Users API'
    });

    this.lambdaSecurityGroup = securityGroup;
    this.migrationHandler = migrationRunner;
    this.userAPI = api;
  }
}
@hecarrillo hecarrillo changed the title Function integration AWS asset duplication Aug 31, 2024
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

No branches or pull requests

1 participant