crowdsec/pkg/database/delete.go
2020-07-30 15:58:06 +02:00

150 lines
4.8 KiB
Go

package database
import (
"fmt"
"time"
"github.com/pkg/errors"
"github.com/crowdsecurity/crowdsec/pkg/types"
log "github.com/sirupsen/logrus"
)
/*try to delete entries with matching fields */
func (c *Context) DeleteBan(target string) (int, error) {
if target != "" {
ret := c.Db.Delete(types.BanApplication{}, "ip_text = ?", target)
if ret.Error != nil {
log.Errorf("Failed to delete record with BanTarget %s : %v", target, ret.Error)
return 0, ret.Error
}
return int(ret.RowsAffected), nil
}
return 0, fmt.Errorf("no target provided")
}
func (c *Context) DeleteAll() error {
allBa := types.BanApplication{}
records := c.Db.Unscoped().Delete(&allBa)
if records.Error != nil {
return records.Error
}
return nil
}
func (c *Context) DeleteExpired() (int, error) {
c.lock.Lock()
defer c.lock.Unlock()
//Delete the expired records
now := time.Now()
count := 0
if c.flush {
retx := c.Db.Delete(types.BanApplication{}, "until < ?", now)
if retx.Error != nil {
return 0, retx.Error
}
if retx.RowsAffected > 0 {
log.Infof("Flushed %d expired entries from Ban Application", retx.RowsAffected)
count = int(retx.RowsAffected)
}
}
return count, nil
}
func (c *Context) CleanUpRecordsByAge() (int, error) {
//let's fetch all expired records that are more than XX days olds
sos := []types.BanApplication{}
if c.maxDurationRetention == 0 {
return 0, nil
}
//look for soft-deleted events that are OLDER than maxDurationRetention
ret := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").
Where("until < ?", time.Now().Add(-c.maxDurationRetention)).
Order("updated_at desc").Find(&sos)
if ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to get count of old records")
}
//no events elligible
if len(sos) == 0 || ret.RowsAffected == 0 {
log.Debugf("no event older than %s", c.maxDurationRetention.String())
return 0, nil
}
/*This is clearly suboptimal, and 'left join' and stuff gives way better results, but doesn't seem to behave equally on sqlite and mysql*/
delRecords := 0
for _, record := range sos {
copy := record
if ret := c.Db.Unscoped().Table("signal_occurences").Where("ID = ?", copy.SignalOccurenceID).Delete(&types.SignalOccurence{}); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean signal_occurences")
}
if ret := c.Db.Unscoped().Table("event_sequences").Where("signal_occurence_id = ?", copy.SignalOccurenceID).Delete(&types.EventSequence{}); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean event_sequences")
}
if ret := c.Db.Unscoped().Table("ban_applications").Delete(&copy); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean ban_applications")
}
delRecords++
}
log.Printf("max_records_age: deleting %d events (max age:%s)", delRecords, c.maxDurationRetention)
return delRecords, nil
}
func (c *Context) CleanUpRecordsByCount() (int, error) {
var count int
if c.maxEventRetention <= 0 {
return 0, nil
}
ret := c.Db.Unscoped().Table("ban_applications").Count(&count)
if ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to get bans count")
}
if count < c.maxEventRetention {
log.Debugf("%d < %d, don't cleanup", count, c.maxEventRetention)
return 0, nil
}
sos := []types.BanApplication{}
now := time.Now()
/*get soft deleted records oldest to youngest*/
//records := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").Where(`strftime("%s", deleted_at) < strftime("%s", "now")`).Find(&sos)
records := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").Where("deleted_at < ?", now).Find(&sos)
if records.Error != nil {
return 0, errors.Wrap(records.Error, "failed to list expired bans for flush")
}
//let's do it in a single transaction
delRecords := 0
for _, ld := range sos {
copy := ld
if ret := c.Db.Unscoped().Table("signal_occurences").Where("ID = ?", copy.SignalOccurenceID).Delete(&types.SignalOccurence{}); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean signal_occurences")
}
if ret := c.Db.Unscoped().Table("event_sequences").Where("signal_occurence_id = ?", copy.SignalOccurenceID).Delete(&types.EventSequence{}); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean event_sequences")
}
if ret := c.Db.Unscoped().Table("ban_applications").Delete(&copy); ret.Error != nil {
return 0, errors.Wrap(ret.Error, "failed to clean ban_applications")
}
//we need to delete associations : event_sequences, signal_occurences
delRecords++
//let's delete as well the associated event_sequence
if count-delRecords <= c.maxEventRetention {
break
}
}
if len(sos) > 0 {
log.Printf("max_records: deleting %d events. (%d soft-deleted)", delRecords, len(sos))
} else {
log.Debugf("didn't find any record to clean")
}
return delRecords, nil
}