How can I handle database schema migration when using lambda and aurora postgresql? - postgresql

I am deploying application on lambda and using aurora postgresql as database. During development process the database schema changes quite frequently and I am looking for a way to migrate the schema. I know that flyway can do the job but it works fine for an application deployed on EC2 instance rather than lambda. What is the best way to do the job in lambda?
I can think of a workaround solution. My lambda is in typescript so it is running inside nodejs environment.

I am using loopback4 to create my models and database schema. I have an AWS Custom resources that's calls the handler to migrate the schema to RDS.
here is what you need to do:
create a security group for RDS
1.1. Add inbound rule: allow from lambda SG on TCP 3306
1.2. Add Outbound rule: allow all protocols, all ports on all
create a security group for the lambda
2.1. Add Outbound rule: allow all protocols, all ports on all
here is my code using CDK:
/** Lambda Security Group */
const lambdaSecurityGroup = new SecurityGroup(this, "LambdaSecurityGroup", {
securityGroupName: "lambda-security-group",
description: "Lambda security group",
vpc: vpc,
allowAllOutbound: true,
});
/** Security Group */
const securityGroup = new SecurityGroup(this, "SecurityGroup", {
securityGroupName: "rds-security-group",
description: "instance security group",
vpc: vpc,
allowAllOutbound: true,
});
/** Security Group Inbound rules - Lambda security group*/
securityGroup.addIngressRule(
SecurityGroup.fromSecurityGroupId(
this,
"LambdaSecurityGroupId",
lambdaSecurityGroup.securityGroupId
),
Port.tcp(config.DatabasePort),
"Allow from Lambda security group on TCP 3306"
);
const customResourceMigrateProvider = new CustomResources.Provider(
this,
"CustomResourceMigrateProvider",
{
onEventHandler: new Function(this, "CustomResourceMigrateLambda", {
runtime: Runtime.NODEJS_12_X,
code: /*this.lambdaCode ||*/ Code.fromAsset("dist"),
handler: "loopback/handlers/custom-resource-migrate.handler",
timeout: Duration.seconds(30),
vpc: vpc,
vpcSubnets: { subnets: [appSubnet1aId, appSubnet1bId] },
securityGroups: [lambdaSecurityGroup],
environment: environmentVariables,
role: customRole,
layers: [layer],
}),
//isCompleteHandler: isComplete,
logRetention: logs.RetentionDays.ONE_DAY,
}
);

I am using loopback4 with lambda and I have created a custom resource that will connect to the RDS and run the migrate script to update the schema.

Related

Scala-Redis: How to connect a global redis cache in Scala?

I am using scala-redis to make a connection to global redis database. I am able to connect easily with the locally running redis server but not sure what to pass in the constructor to connect with the global database. Tried passing username, password and port number but didn't work out.
I am using memcached protocol.
What do I mean by global redis-cache?
I have configured a cache on https://app.redislabs.com/
I am using these credentials:
Could you please share some details?
What is meant by Global Redis Cache? Is that you are trying to access it in AWS?
If in AWS then you have to create a script (Cloud Formation - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_ElastiCache.html) that creates the connection with AWS Redis Cache. Then provide the following config in application-prod.conf.
//NOTE: ELASTICACHE_ENDPOINT is your AWS REDIS endpoint
play.cache.redis {
bind-default: false
default-cache: "redis"
source: standalone
dispatcher: contexts.blockingCacheDispatcher
instances {
cache-name-1 {
connection-string: ${?ELASTICACHE_ENDPOINT}
source: connection-string
database: 1
},
cache-name-2 {
connection-string: ${?ELASTICACHE_ENDPOINT}
source: connection-string
database: 1
}
}
}

How can I set compatibility mode for Amazon OpenSearch using CloudFormation?

Since AWS has replaced ElasticSearch with OpenSearch, some clients have issues connecting to the OpenSearch Service.
To avoid that, we can enable compatibility mode during the cluster creation.
Certain Elasticsearch OSS clients, such as Logstash, check the cluster
version before connecting. Compatibility mode sets OpenSearch to
report its version as 7.10 so that these clients continue to work with
the service.
I'm trying to use CloudFormation to create a cluster using AWS::OpenSearchService::Domain instead of AWS::Elasticsearch::Domain but I can't see a way to enable compatibility mode.
To do this in terraform use the config
resource "aws_elasticsearch_domain" "search" {
domain_name = "search"
advanced_options = {
"override_main_response_version" = "true"
}
}
docs can be found can be found here https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticsearch_domain
The AWS::OpenSearchService::Domain CloudFormation resource has a property called AdvancedOptions.
As per documentation, you should pass override_main_response_version to the advanced options to enable compatibility mode.
Example:
Resources:
OpenSearchServiceDomain:
Type: AWS::OpenSearchService::Domain
Properties:
DomainName: 'test'
EngineVersion: 'OpenSearch_1.0'
AdvancedOptions:
override_main_response_version: true
You can add this in the advanced section tab AdvancedOptions.
Example:
Resources:
OpenSearchServiceDomain:
Type: AWS::OpenSearchService::Domain
Properties:
AdvancedOptions:
override_main_response_version: true

timeout errors from Lambda when trying to access an Amazon RDS DB instance

I am writing a Python app to run as lambda function and want to connect to an RDS DB instance without making it publicly accessible.
The RDS DB instance was already created under the default VPC with security group "sg-abcd".
So I have:
created a lambda function under the same default VPC
created a role with the following permission AWSLambdaVPCAccessExecutionRole and assigned it to the lambda function as in https://docs.aws.amazon.com/lambda/latest/dg/services-rds-tutorial.html
set sg-abcd as the lambda function's security group
added sg-abcd as source in the security group's inbound rules
added the CIDR range of the lambda function's subnet as source in the security group's inbound rules
However, when I invoke the lambda function it times out.
I can connect to the RDS DB from my laptop (after setting my IP as source in the sg's inbound rules), so I now that it is not an authentication problem. Also, for the RDS DB "Publicly Accessible" is set to "Yes".
Here's part of the app's code (where I try to connect):
rds_host = "xxx.rds.amazonaws.com"
port = xxxx
name = rds_config.db_username
password = rds_config.db_password
db_name = rds_config.db_name
logger = logging.getLogger()
logger.setLevel(logging.INFO)
try:
conn = psycopg2.connect(host=rds_host, database=db_name, user=name, password=password, connect_timeout=20)
except psycopg2.Error as e:
logger.error("ERROR: Unexpected error: Could not connect to PostgreSQL instance.")
logger.error(e)
sys.exit()
I really can't understand what I am missing. Any suggestion is welcomed, please help me figure it out!
Edit: the inbound rules that I have set look like this:
Security group rule ID: sgr-123456789
Type Info: PostgreSQL
Protocol Info: TPC
Port range Info: 5432
Source: sg-abcd OR IP or CIDR range
This document should help you out. Just make sure to get the suggestions for your specific scenario, whether the lambda function and the RDS instance are in the same VPC or not.
In my case I have the lambda function and the RDS instance in the same VPC and also both have the same subnets and SGs. But just make sure to follow the instructions in that document for the configurations needed for each scenario.

Is there a way to not allocate an elastic IP (EIP) when creating a VPC using aws-cdk

I'm using the following code to create subnets that will be imported by another stack and used for a dedicated EFS VPC. If I don't create a PUBLIC subnet I get errors on creation. However the side effect is this code allocates an elastic IP address and I don't want one allocated, they are a precious resource.
How do I get rid of the elastic IP address? None of the methods allow you get anything that has an EIP attribute or method:
const fileSystemVpc = new ec2.Vpc(this, 'vpcForEfs', {
subnetConfiguration: [
{
cidrMask: 20,
name: 'nfsisolated',
subnetType: ec2.SubnetType.ISOLATED,
},
{
cidrMask: 20,
name: 'nfsprivate',
subnetType: ec2.SubnetType.PRIVATE,
},
{
cidrMask: 20,
name: 'nfspublic',
subnetType: ec2.SubnetType.PUBLIC,
},
],
});
If I comment out the PUBLIC section I get the following error on creation:
If you configure PRIVATE subnets in 'subnetConfiguration', you must also
configure PUBLIC subnets to put the NAT gateways into (got
[{"cidrMask":20,"name":"nfsisolated","subnetType":"Isolated"},
{"cidrMask":20,"name":"nfsprivate","subnetType":"Private"}].
Relevant issues that don't solve the problem but are similar:
https://github.com/aws/aws-cdk/issues/1305
https://github.com/aws/aws-cdk/issues/3704
This is the commit that added that check: https://github.com/aws/aws-cdk/commit/9a96c37b225b135c9afbf523ed3fbc67cba5ca50
Essentially if CDK wasn't stopping you with that message it would fail when CloudFormation tried to deploy the stack.
Here is more info from AWS on it as well: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html
You can see the description of the ticket that was referenced in it and the AWS docs, if whatever you want to put in the PRIVATE subnets doesn't require internet access you should be using ISOLATED instead. This is because a PRIVATE subnet requires a NatGateway and a NatGateway is required to exist in a PUBLIC subnet with an elastic IP. Again, if you don't require outbound access to the internet from your PRIVATE subnet just use ISOLATED

Cannot get AWS Data Pipeline connected to Redshift

I have a query I'd like to run regularly in Redshift. I've set up an AWS Data Pipeline for it.
My problem is that I cannot figure out how to access Redshift. I keep getting "Unable to establish connection" errors. I have an Ec2Resource and I've tried including a subnet from our cluster's VPC and using the Security Group Id that Redshift uses, while also adding that sg-id to the inbound part of the rules. No luck.
Does anyone have a from-scratch way to set up a data pipeline to run against Redshift?
How I currently have my pipeline set up
RedshiftDatabase
Connection String: jdbc:redshift://[host]:[port]/[database]
Username, Password
Ec2Resource
Resource Role: DataPipelineDefaultResourceRole
Role: DataPipelineDefaultRole
Terminate after: 20 minutes
SqlActivity
Database: [database] (from Connection String)
Runs on: Ec2Resource
Script: SQL query
Error message
Unable to establish connection to jdbc:postgresql://[host]:[port]/[database] Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
Ok, so the answer lies in Security Groups. I had to find the Security Group my Redshift cluster is in, and then add that as a value to "Security Group" parameter on the Ec2Resource in the DataPipeline.
Ec2Resource
Resource Role: DataPipelineDefaultResourceRole
Role: DataPipelineDefaultRole
Terminate after: 20 minutes
Security Group: sg-XXXXX [pull from Redshift]
Try opening inbound rules to all sources, just to narrow down possible causes. You've probably done this, but make sure you've set up your jdbc driver and configurations according to this.