From 0d5d6f1e2b9709f29ff5853a9d522b0744799cc3 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 29 Nov 2023 11:53:16 +0900 Subject: [PATCH] draft changes --- lib/bootstrapper/index.ts | 156 ++++++++++++++++++ .../runtime}/Dockerfile | 0 .../runtime}/handler.py | 0 lib/database/index.ts | 89 +--------- 4 files changed, 158 insertions(+), 87 deletions(-) create mode 100644 lib/bootstrapper/index.ts rename lib/{database/bootstrapper_runtime => bootstrapper/runtime}/Dockerfile (100%) rename lib/{database/bootstrapper_runtime => bootstrapper/runtime}/handler.py (100%) diff --git a/lib/bootstrapper/index.ts b/lib/bootstrapper/index.ts new file mode 100644 index 0000000..444a2bf --- /dev/null +++ b/lib/bootstrapper/index.ts @@ -0,0 +1,156 @@ +import { + Stack, + aws_rds as rds, + aws_ec2 as ec2, + aws_secretsmanager as secretsmanager, + aws_lambda, + CustomResource, + RemovalPolicy, + Duration, + aws_logs, + + } from "aws-cdk-lib"; + import { Construct } from "constructs"; + import { CustomLambdaFunctionProps } from "../utils"; + + const DEFAULT_PGSTAC_VERSION = "0.7.10"; + + function hasVpc( + instance: rds.DatabaseInstance | rds.IDatabaseInstance + ): instance is rds.DatabaseInstance { + return (instance as rds.DatabaseInstance).vpc !== undefined; + } + + /** + * An RDS instance with pgSTAC installed. This is a wrapper around the + * `rds.DatabaseInstance` higher-level construct making use + * of the BootstrapPgStac construct. + */ + export class PgStacDatabaseBootstrapper extends Construct { + pgstacSecret: secretsmanager.ISecret; + + constructor(scope: Construct, id: string, props: PgStacDatabaseBootstrapperProps) { + super(scope, id); + + + + const handler = new aws_lambda.Function(this, "lambda", { + // defaults for configurable properties + runtime: aws_lambda.Runtime.PYTHON_3_11, + handler: "handler.handler", + memorySize: 128, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.minutes(2), + code: aws_lambda.Code.fromDockerBuild(__dirname, { + file: "bootstrapper_runtime/Dockerfile", + buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.11"} + }), + // overwrites defaults with user-provided configurable properties + ...props.bootstrapperLambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user + vpc: hasVpc(props.db) ? props.db.vpc : props.vpc, + allowPublicSubnet: true + }); + + this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { + secretName: [ + props.secretsPrefix || "pgstac", + id, + this.node.addr.slice(-8), + ].join("/"), + generateSecretString: { + secretStringTemplate: JSON.stringify({ + dbname: props.pgstacDbName || "pgstac", + engine: "postgres", + port: 5432, + host: props.db.instanceEndpoint.hostname, + username: props.pgstacUsername || "pgstac_user", + }), + generateStringKey: "password", + excludePunctuation: true, + }, + description: `PgSTAC database bootstrapped by ${ + Stack.of(this).stackName + }`, + }); + + // Allow lambda to... + // read new user secret + this.pgstacSecret.grantRead(handler); + // read database secret + props.db.secret!.grantRead(handler); + // connect to database + props.db.connections.allowFrom(handler, ec2.Port.tcp(5432)); + + let customResourceProperties : { [key: string]: any} = {}; + + // if customResourceProperties are provided, fill in the values. + if (props.customResourceProperties) { + Object.assign(customResourceProperties, props.customResourceProperties); + } + + // update properties + customResourceProperties["conn_secret_arn"] = props.db.secret!.secretArn; + customResourceProperties["new_user_secret_arn"] = this.pgstacSecret.secretArn; + + // if props.lambdaFunctionOptions doesn't have 'code' defined, update pgstac_version (needed for default runtime) + if (!props.bootstrapperLambdaFunctionOptions?.code) { + customResourceProperties["pgstac_version"] = DEFAULT_PGSTAC_VERSION; + } + // this.connections = props.database.connections; + new CustomResource(this, "bootstrapper", { + serviceToken: handler.functionArn, + properties: customResourceProperties, + removalPolicy: RemovalPolicy.RETAIN, // This retains the custom resource (which doesn't really exist), not the database + }); + + } + + } + + export interface PgStacDatabaseBootstrapperProps extends rds.DatabaseInstanceProps { + + /** + * RDS instance to install pgSTAC on. + */ + readonly db: rds.DatabaseInstance; + + + /** + * Name of database that is to be created and onto which pgSTAC will be installed. + * + * @default pgstac + */ + readonly pgstacDbName?: string; + + /** + * Prefix to assign to the generated `secrets_manager.Secret` + * + * @default pgstac + */ + readonly secretsPrefix?: string; + + /** + * Name of user that will be generated for connecting to the pgSTAC database. + * + * @default pgstac_user + */ + readonly pgstacUsername?: string; + + /** + * Lambda function Custom Resource properties. A custom resource property is going to be created + * to trigger the boostrapping lambda function. This parameter allows the user to specify additional properties + * on top of the defaults ones. + * + */ + readonly customResourceProperties?: { + [key: string]: any; + } + + /** + * Optional settings for the bootstrapper lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * + * @default - defined in the construct. + */ + readonly bootstrapperLambdaFunctionOptions?: CustomLambdaFunctionProps; + } \ No newline at end of file diff --git a/lib/database/bootstrapper_runtime/Dockerfile b/lib/bootstrapper/runtime/Dockerfile similarity index 100% rename from lib/database/bootstrapper_runtime/Dockerfile rename to lib/bootstrapper/runtime/Dockerfile diff --git a/lib/database/bootstrapper_runtime/handler.py b/lib/bootstrapper/runtime/handler.py similarity index 100% rename from lib/database/bootstrapper_runtime/handler.py rename to lib/bootstrapper/runtime/handler.py diff --git a/lib/database/index.ts b/lib/database/index.ts index 481f322..dc7b8d6 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -1,26 +1,11 @@ import { Stack, - aws_rds as rds, - aws_ec2 as ec2, - aws_secretsmanager as secretsmanager, - aws_lambda, - CustomResource, - RemovalPolicy, - Duration, - aws_logs, - + aws_rds as rds } from "aws-cdk-lib"; import { Construct } from "constructs"; import { CustomLambdaFunctionProps } from "../utils"; const instanceSizes: Record = require("./instance-memory.json"); -const DEFAULT_PGSTAC_VERSION = "0.7.10"; - -function hasVpc( - instance: rds.DatabaseInstance | rds.IDatabaseInstance -): instance is rds.DatabaseInstance { - return (instance as rds.DatabaseInstance).vpc !== undefined; -} /** * An RDS instance with pgSTAC installed. This is a wrapper around the @@ -29,7 +14,6 @@ function hasVpc( */ export class PgStacDatabase extends Construct { db: rds.DatabaseInstance; - pgstacSecret: secretsmanager.ISecret; constructor(scope: Construct, id: string, props: PgStacDatabaseProps) { super(scope, id); @@ -59,76 +43,7 @@ export class PgStacDatabase extends Construct { ...props, }); - const handler = new aws_lambda.Function(this, "lambda", { - // defaults for configurable properties - runtime: aws_lambda.Runtime.PYTHON_3_11, - handler: "handler.handler", - memorySize: 128, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.minutes(2), - code: aws_lambda.Code.fromDockerBuild(__dirname, { - file: "bootstrapper_runtime/Dockerfile", - buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.11"} - }), - // overwrites defaults with user-provided configurable properties - ...props.bootstrapperLambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user - vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, - allowPublicSubnet: true - }); - - this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { - secretName: [ - props.secretsPrefix || "pgstac", - id, - this.node.addr.slice(-8), - ].join("/"), - generateSecretString: { - secretStringTemplate: JSON.stringify({ - dbname: props.pgstacDbName || "pgstac", - engine: "postgres", - port: 5432, - host: this.db.instanceEndpoint.hostname, - username: props.pgstacUsername || "pgstac_user", - }), - generateStringKey: "password", - excludePunctuation: true, - }, - description: `PgSTAC database bootstrapped by ${ - Stack.of(this).stackName - }`, - }); - - // Allow lambda to... - // read new user secret - this.pgstacSecret.grantRead(handler); - // read database secret - this.db.secret!.grantRead(handler); - // connect to database - this.db.connections.allowFrom(handler, ec2.Port.tcp(5432)); - - let customResourceProperties : { [key: string]: any} = {}; - - // if customResourceProperties are provided, fill in the values. - if (props.customResourceProperties) { - Object.assign(customResourceProperties, props.customResourceProperties); - } - - // update properties - customResourceProperties["conn_secret_arn"] = this.db.secret!.secretArn; - customResourceProperties["new_user_secret_arn"] = this.pgstacSecret.secretArn; - - // if props.lambdaFunctionOptions doesn't have 'code' defined, update pgstac_version (needed for default runtime) - if (!props.bootstrapperLambdaFunctionOptions?.code) { - customResourceProperties["pgstac_version"] = DEFAULT_PGSTAC_VERSION; - } - // this.connections = props.database.connections; - new CustomResource(this, "bootstrapper", { - serviceToken: handler.functionArn, - properties: customResourceProperties, - removalPolicy: RemovalPolicy.RETAIN, // This retains the custom resource (which doesn't really exist), not the database - }); - + } public getParameters(