Skip to content

Commit

Permalink
Merge pull request #1 from Qovery/elasticcache
Browse files Browse the repository at this point in the history
feat: adding Elasticache support
  • Loading branch information
deimosfr authored Nov 20, 2020
2 parents 6a43b7e + 54dc151 commit 3eded91
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 17 deletions.
4 changes: 2 additions & 2 deletions charts/pleco/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ name: pleco
description: Automatically removes Cloud managed services and Kubernetes resources based on tags with TTL
type: application
home: https://github.com/Qovery/pleco
version: 0.1.8
appVersion: 0.1.8
version: 0.2.0
appVersion: 0.2.0
icon: https://github.com/Qovery/pleco/raw/main/assets/pleco_logo.png
2 changes: 1 addition & 1 deletion charts/pleco/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ replicaCount: 1
image:
repository: qoveryrd/pleco
pullPolicy: IfNotPresent
plecoImageTag: "v0.1.8"
plecoImageTag: "v0.2.0"

environmentVariables:
CHECK_INTERVAL: "120"
Expand Down
2 changes: 1 addition & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ func init() {
}

func GetCurrentVersion() string {
return "0.1.8" // ci-version-check
return "0.2.0" // ci-version-check
}
10 changes: 8 additions & 2 deletions core/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@ func StartDaemon(dryRun bool, interval int64) {
}

currentRdsSession := aws.RdsSession(*currentSession, os.Getenv("AWS_DEFAULT_REGION"))
currentElasticacheSession := aws.ElasticacheSession(*currentSession, os.Getenv("AWS_DEFAULT_REGION"))

for {
// check RDS
err = aws.DeleteExpiredDatabases(*currentRdsSession, "ttl", dryRun)
err = aws.DeleteExpiredRDSDatabases(*currentRdsSession, "ttl", dryRun)
if err != nil {
log.Error(err)
}
// check DocumentDB
err = aws.DeleteExpiredClusters(*currentRdsSession, "ttl", dryRun)
err = aws.DeleteExpiredDocumentDBClusters(*currentRdsSession, "ttl", dryRun)
if err != nil {
log.Error(err)
}
// check Elasticache
err = aws.DeleteExpiredElasticacheDatabases(*currentElasticacheSession, "ttl", dryRun)
if err != nil {
log.Error(err)
}
Expand Down
12 changes: 6 additions & 6 deletions providers/aws/documentdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type documentDBCluster struct {
TTL int64
}

func listTaggedClusters(svc rds.RDS, tagName string) ([]documentDBCluster, error) {
func listTaggedDocumentDBClusters(svc rds.RDS, tagName string) ([]documentDBCluster, error) {
var taggedClusters []documentDBCluster
var instances []string

Expand Down Expand Up @@ -69,7 +69,7 @@ func listTaggedClusters(svc rds.RDS, tagName string) ([]documentDBCluster, error
return taggedClusters, nil
}

func deleteCluster(svc rds.RDS, cluster documentDBCluster, dryRun bool) error {
func deleteDocumentDBCluster(svc rds.RDS, cluster documentDBCluster, dryRun bool) error {
deleteInstancesErrors := 0

if cluster.Status == "deleting" {
Expand All @@ -90,7 +90,7 @@ func deleteCluster(svc rds.RDS, cluster documentDBCluster, dryRun bool) error {
continue
}

err = deleteDatabase(svc, rdsInstanceInfo, dryRun)
err = deleteRDSDatabase(svc, rdsInstanceInfo, dryRun)
if err != nil {
log.Errorf("Deletion error on DocumentDB instance %s/%s/%s: %s",
instance, cluster.DBClusterIdentifier, *svc.Config.Region, err)
Expand Down Expand Up @@ -121,15 +121,15 @@ func deleteCluster(svc rds.RDS, cluster documentDBCluster, dryRun bool) error {
return nil
}

func DeleteExpiredClusters(svc rds.RDS, tagName string, dryRun bool) error {
clusters, err := listTaggedClusters(svc, tagName)
func DeleteExpiredDocumentDBClusters(svc rds.RDS, tagName string, dryRun bool) error {
clusters, err := listTaggedDocumentDBClusters(svc, tagName)
if err != nil {
return errors.New(fmt.Sprintf("can't list DocumentDB databases: %s\n", err))
}

for _, cluster := range clusters {
if utils.CheckIfExpired(cluster.ClusterCreateTime, cluster.TTL) {
err := deleteCluster(svc, cluster, dryRun)
err := deleteDocumentDBCluster(svc, cluster, dryRun)
if err != nil {
log.Errorf("Deletion DocumentDB cluster error %s/%s: %s",
cluster.DBClusterIdentifier, *svc.Config.Region, err)
Expand Down
148 changes: 148 additions & 0 deletions providers/aws/elasticache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package aws

import (
"errors"
"fmt"
"github.com/Qovery/pleco/utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/elasticache"
log "github.com/sirupsen/logrus"
"strconv"
"time"
)

type elasticacheCluster struct {
ClusterIdentifier string
ReplicationGroupId string
ClusterCreateTime time.Time
ClusterStatus string
TTL int64
}

func ElasticacheSession(sess session.Session, region string) *elasticache.ElastiCache {
return elasticache.New(&sess, &aws.Config{Region: aws.String(region)})
}

func listTaggedElasticacheDatabases(svc elasticache.ElastiCache, tagName string) ([]elasticacheCluster, error) {
var taggedClusters []elasticacheCluster

log.Debugf("Listing all Elasticache clusters")
result, err := svc.DescribeCacheClusters(nil)
if err != nil {
return nil, err
}

if len(result.CacheClusters) == 0 {
log.Debug("No Elasticache clusters were found")
return nil, nil
}

for _, cluster := range result.CacheClusters {
tags, err := svc.ListTagsForResource(
&elasticache.ListTagsForResourceInput{
ResourceName: aws.String(*cluster.ARN),
},
)
if err != nil {
if *cluster.CacheClusterStatus == "available" {
log.Errorf("Can't get tags for Elasticache cluster: %s", cluster.CacheClusterId)
}
continue
}

for _, tag := range tags.TagList {
if *tag.Key == tagName {
if *tag.Key == "" {
log.Warn("Tag %s was empty and it wasn't expected, skipping", tag.Key)
continue
}

ttl, err := strconv.Atoi(*tag.Value)
if err != nil {
log.Errorf("Error while trying to convert tag value (%s) to integer on instance %s in %s",
*tag.Value, *cluster.CacheClusterId, svc.Config.Region)
continue
}

// required for replicas deletion
replicationGroupId := ""
if cluster.ReplicationGroupId != nil {
replicationGroupId = *cluster.ReplicationGroupId
}

taggedClusters = append(taggedClusters, elasticacheCluster{
ClusterIdentifier: *cluster.CacheClusterId,
ReplicationGroupId: replicationGroupId,
ClusterCreateTime: *cluster.CacheClusterCreateTime,
ClusterStatus: *cluster.CacheClusterStatus,
TTL: int64(ttl),
})
}
}
}
log.Debugf("Found %d Elasticache cluster(s) in ready status with ttl tag", len(taggedClusters))

return taggedClusters, nil
}

func deleteElasticacheCluster(svc elasticache.ElastiCache, cluster elasticacheCluster, dryRun bool) error {
if cluster.ClusterStatus == "deleting" {
log.Infof("Elasticache cluster %s is already in deletion process, skipping...", cluster.ClusterIdentifier)
return nil
} else {
log.Infof("Deleting Elasticache cluster %s in %s, expired after %d seconds",
cluster.ClusterIdentifier, *svc.Config.Region, cluster.TTL)
}

if dryRun {
return nil
}

// with replicas
if cluster.ReplicationGroupId != "" {
_, err := svc.DeleteReplicationGroup(
&elasticache.DeleteReplicationGroupInput{
ReplicationGroupId: aws.String(cluster.ReplicationGroupId),
RetainPrimaryCluster: aws.Bool(false),
},
)
if err != nil {
return err
}
}

_, err := svc.DeleteCacheCluster(
&elasticache.DeleteCacheClusterInput{
CacheClusterId: aws.String(cluster.ClusterIdentifier),
},
)
if err != nil {
return err
}

return nil
}

func DeleteExpiredElasticacheDatabases(svc elasticache.ElastiCache, tagName string, dryRun bool) error {
clusters, err := listTaggedElasticacheDatabases(svc, tagName)
if err != nil {
return errors.New(fmt.Sprintf("can't list Elasticache databases: %s\n", err))
}

for _, cluster := range clusters {
if utils.CheckIfExpired(cluster.ClusterCreateTime, cluster.TTL) {
err := deleteElasticacheCluster(svc, cluster, dryRun)
if err != nil {
log.Errorf("Deletion Elasticache cluster error %s/%s: %s",
cluster.ClusterIdentifier, *svc.Config.Region, err)
continue
}
} else {
log.Debugf("Elasticache cluster %s in %s, has not yet expired",
cluster.ClusterIdentifier, *svc.Config.Region)
}
}

return nil
}
10 changes: 5 additions & 5 deletions providers/aws/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func RdsSession(sess session.Session, region string) *rds.RDS {
return rds.New(&sess, &aws.Config{Region: aws.String(region)})
}

func listTaggedDatabases(svc rds.RDS, tagName string) ([]rdsDatabase, error) {
func listTaggedRDSDatabases(svc rds.RDS, tagName string) ([]rdsDatabase, error) {
var taggedDatabases []rdsDatabase

log.Debugf("Listing all RDS databases")
Expand Down Expand Up @@ -72,7 +72,7 @@ func listTaggedDatabases(svc rds.RDS, tagName string) ([]rdsDatabase, error) {
return taggedDatabases, nil
}

func deleteDatabase(svc rds.RDS, database rdsDatabase, dryRun bool) error {
func deleteRDSDatabase(svc rds.RDS, database rdsDatabase, dryRun bool) error {
if database.DBInstanceStatus == "deleting" {
log.Infof("RDS instance %s is already in deletion process, skipping...", database.DBInstanceIdentifier)
return nil
Expand Down Expand Up @@ -122,15 +122,15 @@ func getRDSInstanceInfos(svc rds.RDS, databaseIdentifier string) (rdsDatabase, e
}, nil
}

func DeleteExpiredDatabases(svc rds.RDS, tagName string, dryRun bool) error {
databases, err := listTaggedDatabases(svc, tagName)
func DeleteExpiredRDSDatabases(svc rds.RDS, tagName string, dryRun bool) error {
databases, err := listTaggedRDSDatabases(svc, tagName)
if err != nil {
return errors.New(fmt.Sprintf("can't list RDS databases: %s\n", err))
}

for _, database := range databases {
if utils.CheckIfExpired(database.InstanceCreateTime, database.TTL) {
err := deleteDatabase(svc, database, dryRun)
err := deleteRDSDatabase(svc, database, dryRun)
if err != nil {
log.Errorf("Deletion RDS database error %s/%s: %s",
database.DBInstanceIdentifier, *svc.Config.Region, err)
Expand Down

0 comments on commit 3eded91

Please sign in to comment.