Skip to content

Commit

Permalink
Add provision to create and delete DBSubnetGroups for RDS integration…
Browse files Browse the repository at this point in the history
… test (#1950)

* Update RDS postgres app implementation, execute postgres command from Deployment

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Fix Lint Remove unnecessary trailing new line

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Initialize testWorkloadName field

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Move BastionWorkload function to be used as utility

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Remove export of BastionWorkload() utility function

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Add app name in error messages

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Update RDS Aurora app implementation, execute mysql query from Deployment

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Remove unused methods from rds aurora app

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Add helper methods to add DBSubnetGroup

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* add callers to create and delete dbsubnetGroups in rds postgres app test

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* add callers to create and delete dbsubnetGroups in rds aurora app test

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Refactor code

* Refactor code

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* remove duplicate import

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* minor refactor

* correct indentation

* Fix error

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Update RDS Aurora app implementation, execute mysql query from Deployment

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Remove unused methods from rds aurora app

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* minor refactor

* Minor refactor

* Pass VpcId while creating security group

* Use securityGroup ID instead of security group name to authorizeSecurityGroupIngress

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor Fix

* [Refactor] Add function to get VPCId for RDS apps

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Remove unnecessary comment

* Refactor code

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* Minor refactor

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

* improve comments

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>

---------

Signed-off-by: Akanksha Kumari <akankshakumari393@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
akankshakumari393 and mergify[bot] committed Mar 17, 2023
1 parent f29e81b commit d23ccdf
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 22 deletions.
48 changes: 38 additions & 10 deletions pkg/app/rds_aurora_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
rdserr "github.com/aws/aws-sdk-go/service/rds"
awsrds "github.com/aws/aws-sdk-go/service/rds"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -53,6 +53,7 @@ type RDSAuroraMySQLDB struct {
id string
host string
dbName string
dbSubnetGroup string
username string
password string
accessID string
Expand All @@ -62,6 +63,7 @@ type RDSAuroraMySQLDB struct {
securityGroupID string
securityGroupName string
bastionDebugWorkloadName string
vpcID string
}

func NewRDSAuroraMySQLDB(name, region string) App {
Expand Down Expand Up @@ -138,25 +140,37 @@ func (a *RDSAuroraMySQLDB) Install(ctx context.Context, namespace string) error
if err := kube.WaitOnDeploymentReady(ctx, a.cli, a.namespace, a.bastionDebugWorkloadName); err != nil {
return errors.Wrapf(err, "Failed while waiting for deployment %s to be ready, app=%s", a.bastionDebugWorkloadName, a.name)
}

rdsCli, err := rds.NewClient(ctx, awsConfig, region)
if err != nil {
return err
}

a.vpcID, err = vpcIDForRDSInstance(ctx, ec2Cli)
if err != nil {
return err
}

dbSubnetGroup, err := dbSubnetGroup(ctx, ec2Cli, rdsCli, a.vpcID, a.name, subnetGroupDescription)
if err != nil {
return err
}
a.dbSubnetGroup = dbSubnetGroup

// Create security group
log.Info().Print("Creating security group.", field.M{"app": a.name, "name": a.securityGroupName})
sg, err := ec2Cli.CreateSecurityGroup(ctx, a.securityGroupName, "To allow ingress to Aurora DB cluster")
sg, err := ec2Cli.CreateSecurityGroup(ctx, a.securityGroupName, "To allow ingress to Aurora DB cluster", a.vpcID)
if err != nil {
return errors.Wrap(err, "Error creating security group")
}
a.securityGroupID = *sg.GroupId

// Add ingress rule
_, err = ec2Cli.AuthorizeSecurityGroupIngress(ctx, a.securityGroupName, "0.0.0.0/0", "tcp", 3306)
_, err = ec2Cli.AuthorizeSecurityGroupIngress(ctx, a.securityGroupID, "0.0.0.0/0", "tcp", 3306)
if err != nil {
return errors.Wrap(err, "Error authorizing security group")
}

rdsCli, err := rds.NewClient(ctx, awsConfig, region)
if err != nil {
return err
}

// Create RDS instance
log.Info().Print("Creating RDS Aurora DB cluster.", field.M{"app": a.name, "id": a.id})
_, err = rdsCli.CreateDBCluster(ctx, AuroraDBStorage, AuroraDBInstanceClass, a.id, string(function.DBEngineAuroraMySQL), a.dbName, a.username, a.password, []string{a.securityGroupID})
Expand Down Expand Up @@ -309,7 +323,7 @@ func (a *RDSAuroraMySQLDB) Uninstall(ctx context.Context) error {
descOp, err := rdsCli.DescribeDBClusters(ctx, a.id)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
if aerr.Code() != rdserr.ErrCodeDBClusterNotFoundFault {
if aerr.Code() != awsrds.ErrCodeDBClusterNotFoundFault {
return err
}
log.Print("Aurora DB cluster is not found")
Expand All @@ -327,6 +341,20 @@ func (a *RDSAuroraMySQLDB) Uninstall(ctx context.Context) error {
return errors.Wrap(err, "Failed to create ec2 client.")
}

log.Info().Print("Deleting db subnet group.", field.M{"app": a.name})
_, err = rdsCli.DeleteDBSubnetGroup(ctx, a.dbSubnetGroup)
if err != nil {
// If the subnet group does not exist, ignore the error and return
if err, ok := err.(awserr.Error); ok {
switch err.Code() {
case awsrds.ErrCodeDBSubnetGroupNotFoundFault:
log.Info().Print("Subnet Group Does not exist: ErrCodeDBSubnetGroupNotFoundFault.", field.M{"app": a.name, "name": a.dbSubnetGroup})
default:
return errors.Wrapf(err, "Failed to delete subnet group. You may need to delete it manually. app=%s name=%s", a.name, a.dbSubnetGroup)
}
}
}

// delete security group
log.Info().Print("Deleting security group.", field.M{"app": a.name})
_, err = ec2Cli.DeleteSecurityGroup(ctx, a.securityGroupID)
Expand All @@ -336,7 +364,7 @@ func (a *RDSAuroraMySQLDB) Uninstall(ctx context.Context) error {
case "InvalidGroup.NotFound":
log.Error().Print("Security group already deleted: InvalidGroup.NotFound.", field.M{"app": a.name, "name": a.securityGroupName})
default:
return errors.Wrapf(err, "Failed to delete security group. You may need to delete it manually. app=rds-postgresql name=%s", a.securityGroupName)
return errors.Wrapf(err, "Failed to delete security group. You may need to delete it manually. app=%s name=%s", a.name, a.securityGroupName)
}
}
}
Expand Down
46 changes: 37 additions & 9 deletions pkg/app/rds_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type RDSPostgresDB struct {
id string
host string
databases []string
dbSubnetGroup string
username string
password string
accessID string
Expand All @@ -58,11 +59,13 @@ type RDSPostgresDB struct {
configMapName string
secretName string
bastionDebugWorkloadName string
vpcID string
}

const (
dbInstanceType = "db.t3.micro"
postgresConnectionString = "PGPASSWORD=%s psql -h %s -p 5432 -U %s -d %s -t -c"
subnetGroupDescription = "kanister-test-subnet-group"
)

func NewRDSPostgresDB(name string, customRegion string) App {
Expand Down Expand Up @@ -124,12 +127,17 @@ func (pdb *RDSPostgresDB) Install(ctx context.Context, ns string) error {
if err != nil {
return errors.Wrapf(err, "app=%s", pdb.name)
}
// Create ec2 client

ec2Cli, err := ec2.NewClient(ctx, awsConfig, region)
if err != nil {
return err
}

rdsCli, err := rds.NewClient(ctx, awsConfig, region)
if err != nil {
return err
}

pdb.bastionDebugWorkloadName = fmt.Sprintf("%s-workload", pdb.name)

deploymentSpec := bastionDebugWorkloadSpec(ctx, pdb.bastionDebugWorkloadName, "postgres", pdb.namespace)
Expand All @@ -141,23 +149,29 @@ func (pdb *RDSPostgresDB) Install(ctx context.Context, ns string) error {
if err := kube.WaitOnDeploymentReady(ctx, pdb.cli, pdb.namespace, pdb.bastionDebugWorkloadName); err != nil {
return errors.Wrapf(err, "Failed while waiting for deployment %s to be ready, app: %s", pdb.bastionDebugWorkloadName, pdb.name)
}

pdb.vpcID, err = vpcIDForRDSInstance(ctx, ec2Cli)
if err != nil {
return err
}

dbSubnetGroup, err := dbSubnetGroup(ctx, ec2Cli, rdsCli, pdb.vpcID, pdb.name, subnetGroupDescription)
if err != nil {
return err
}
pdb.dbSubnetGroup = dbSubnetGroup

// Create security group
log.Info().Print("Creating security group.", field.M{"app": pdb.name, "name": pdb.securityGroupName})
sg, err := ec2Cli.CreateSecurityGroup(ctx, pdb.securityGroupName, "kanister-test-security-group")
sg, err := ec2Cli.CreateSecurityGroup(ctx, pdb.securityGroupName, "kanister-test-security-group", pdb.vpcID)
if err != nil {
return err
}
pdb.securityGroupID = *sg.GroupId

// Add ingress rule
log.Info().Print("Adding ingress rule to security group.", field.M{"app": pdb.name})
_, err = ec2Cli.AuthorizeSecurityGroupIngress(ctx, pdb.securityGroupName, "0.0.0.0/0", "tcp", 5432)
if err != nil {
return err
}

// Create rds client
rdsCli, err := rds.NewClient(ctx, awsConfig, region)
_, err = ec2Cli.AuthorizeSecurityGroupIngress(ctx, pdb.securityGroupID, "0.0.0.0/0", "tcp", 5432)
if err != nil {
return err
}
Expand Down Expand Up @@ -399,6 +413,20 @@ func (pdb RDSPostgresDB) Uninstall(ctx context.Context) error {
return errors.Wrap(err, "Failed to ec2 client. You may need to delete EC2 resources manually. app=rds-postgresql")
}

log.Info().Print("Deleting db subnet group.", field.M{"app": pdb.name})
_, err = rdsCli.DeleteDBSubnetGroup(ctx, pdb.dbSubnetGroup)
if err != nil {
// If the subnet group does not exist, ignore the error and return
if err, ok := err.(awserr.Error); ok {
switch err.Code() {
case awsrds.ErrCodeDBSubnetGroupNotFoundFault:
log.Info().Print("Subnet Group Does not exist: ErrCodeDBSubnetGroupNotFoundFault.", field.M{"app": pdb.name, "id": pdb.id})
default:
return errors.Wrapf(err, "Failed to delete db subnet group. You may need to delete it manually. app=rds-postgresql name=%s", pdb.dbSubnetGroup)
}
}
}

// Delete security group
log.Info().Print("Deleting security group.", field.M{"app": pdb.name})
_, err = ec2Cli.DeleteSecurityGroup(ctx, pdb.securityGroupID)
Expand Down
45 changes: 45 additions & 0 deletions pkg/app/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ package app
import (
"context"
"fmt"
"os"

"github.com/kanisterio/kanister/pkg/aws/ec2"
"github.com/kanisterio/kanister/pkg/aws/rds"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -94,3 +98,44 @@ func bastionDebugWorkloadSpec(ctx context.Context, name string, image string, na
},
}
}

// vpcIdForRDSInstance gets the VPC ID from env var `VPC_ID` if set, or from the default VPC
func vpcIDForRDSInstance(ctx context.Context, ec2Cli *ec2.EC2) (string, error) {
vpcID := os.Getenv("VPC_ID")

// VPCId is not provided, use Default VPC
if vpcID != "" {
return vpcID, nil
}
defaultVpc, err := ec2Cli.DescribeDefaultVpc(ctx)
if err != nil {
return "", err
}
if len(defaultVpc.Vpcs) == 0 {
return "", fmt.Errorf("No default VPC found")
}
return *defaultVpc.Vpcs[0].VpcId, nil
}

// dbSubnetGroup gets the DBSubnetGroup based on VPC ID
func dbSubnetGroup(ctx context.Context, ec2Cli *ec2.EC2, rdsCli *rds.RDS, vpcID, name, subnetGroupDescription string) (string, error) {
// describe subnets in the VPC
resp, err := ec2Cli.DescribeSubnets(ctx, vpcID)
if err != nil {
return "", errors.Wrapf(err, "Failed to describe subnets")
}

// Extract subnet IDs from the response
var subnetIDs []string
for _, subnet := range resp.Subnets {
subnetIDs = append(subnetIDs, *subnet.SubnetId)
}

// create a subnetgroup with subnets in the VPC
subnetGroup, err := rdsCli.CreateDBSubnetGroup(ctx, fmt.Sprintf("%s-subnetgroup", name), subnetGroupDescription, subnetIDs)
if err != nil {
return "", errors.Wrapf(err, "Failed to create subnet group")
}

return *subnetGroup.DBSubnetGroup.DBSubnetGroupName, nil
}
33 changes: 30 additions & 3 deletions pkg/aws/ec2/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,20 @@ func (e EC2) DescribeSecurityGroup(ctx context.Context, groupName string) (*ec2.
return e.DescribeSecurityGroupsWithContext(ctx, sgi)
}

func (e EC2) CreateSecurityGroup(ctx context.Context, groupName, description string) (*ec2.CreateSecurityGroupOutput, error) {
func (e EC2) CreateSecurityGroup(ctx context.Context, groupName, description, vpcId string) (*ec2.CreateSecurityGroupOutput, error) {
sgi := &ec2.CreateSecurityGroupInput{
DryRun: &e.DryRun,
Description: &description,
GroupName: &groupName,
VpcId: aws.String(vpcId),
}
return e.CreateSecurityGroupWithContext(ctx, sgi)
}

func (e EC2) AuthorizeSecurityGroupIngress(ctx context.Context, groupName, cidr, protocol string, port int64) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
func (e EC2) AuthorizeSecurityGroupIngress(ctx context.Context, groupId, cidr, protocol string, port int64) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
sgi := &ec2.AuthorizeSecurityGroupIngressInput{
DryRun: &e.DryRun,
GroupName: &groupName,
GroupId: &groupId,
CidrIp: &cidr,
IpProtocol: &protocol,
ToPort: &port,
Expand All @@ -78,3 +79,29 @@ func (e EC2) DeleteSecurityGroup(ctx context.Context, groupId string) (*ec2.Dele
}
return e.DeleteSecurityGroupWithContext(ctx, sgi)
}

func (e EC2) DescribeSubnets(ctx context.Context, vpcId string) (*ec2.DescribeSubnetsOutput, error) {
paramsEC2 := &ec2.DescribeSubnetsInput{
Filters: []*ec2.Filter{
{
Name: aws.String("vpc-id"),
Values: []*string{aws.String(vpcId)},
},
},
}
return e.DescribeSubnetsWithContext(ctx, paramsEC2)
}

func (e EC2) DescribeDefaultVpc(ctx context.Context) (*ec2.DescribeVpcsOutput, error) {
vpci := &ec2.DescribeVpcsInput{
Filters: []*ec2.Filter{
{
Name: aws.String("isDefault"),
Values: []*string{
aws.String("true"),
},
},
},
}
return e.DescribeVpcsWithContext(ctx, vpci)
}
27 changes: 27 additions & 0 deletions pkg/aws/rds/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,33 @@ func (r RDS) DeleteDBCluster(ctx context.Context, instanceID string) (*rds.Delet
return r.DeleteDBClusterWithContext(ctx, ddbc)
}

func (r RDS) CreateDBSubnetGroup(ctx context.Context, dbSubnetGroupName, dbSubnetGroupDescription string, subnetIDs []string) (*rds.CreateDBSubnetGroupOutput, error) {
var subnetIds []*string
for _, ID := range subnetIDs {
subnetIds = append(subnetIds, aws.String(ID))
}
dbsgi := &rds.CreateDBSubnetGroupInput{
DBSubnetGroupName: aws.String(dbSubnetGroupName),
DBSubnetGroupDescription: aws.String(dbSubnetGroupDescription),
SubnetIds: subnetIds,
}
return r.CreateDBSubnetGroupWithContext(ctx, dbsgi)
}

func (r RDS) DeleteDBSubnetGroup(ctx context.Context, dbSubnetGroupName string) (*rds.DeleteDBSubnetGroupOutput, error) {
dbsgi := &rds.DeleteDBSubnetGroupInput{
DBSubnetGroupName: aws.String(dbSubnetGroupName),
}
return r.DeleteDBSubnetGroupWithContext(ctx, dbsgi)
}

func (r RDS) DescribeDBSnapshot(ctx context.Context, snapshotID string) (*rds.DescribeDBSnapshotsOutput, error) {
dsi := &rds.DescribeDBSnapshotsInput{
DBSnapshotIdentifier: &snapshotID,
}
return r.DescribeDBSnapshotsWithContext(ctx, dsi)
}

func (r RDS) CreateDBSnapshot(ctx context.Context, instanceID, snapshotID string) (*rds.CreateDBSnapshotOutput, error) {
sni := &rds.CreateDBSnapshotInput{
DBInstanceIdentifier: &instanceID,
Expand Down

0 comments on commit d23ccdf

Please sign in to comment.