bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/database/alerts.go (about) 1 package database 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strconv" 7 "strings" 8 "time" 9 10 "bitbucket.org/Aishee/synsec/pkg/database/ent" 11 "bitbucket.org/Aishee/synsec/pkg/database/ent/alert" 12 "bitbucket.org/Aishee/synsec/pkg/database/ent/decision" 13 "bitbucket.org/Aishee/synsec/pkg/database/ent/event" 14 "bitbucket.org/Aishee/synsec/pkg/database/ent/meta" 15 "bitbucket.org/Aishee/synsec/pkg/models" 16 "bitbucket.org/Aishee/synsec/pkg/types" 17 "github.com/davecgh/go-spew/spew" 18 "github.com/pkg/errors" 19 log "github.com/sirupsen/logrus" 20 ) 21 22 const ( 23 paginationSize = 100 // used to queryAlert to avoid 'too many SQL variable' 24 defaultLimit = 100 // default limit of element to returns when query alerts 25 bulkSize = 50 // bulk size when create alerts 26 ) 27 28 func formatAlertAsString(machineId string, alert *models.Alert) []string { 29 var retStr []string 30 31 /**/ 32 src := "" 33 if alert.Source != nil { 34 if *alert.Source.Scope == types.Ip { 35 src = fmt.Sprintf("ip %s", *alert.Source.Value) 36 if alert.Source.Cn != "" { 37 src += " (" + alert.Source.Cn 38 if alert.Source.AsNumber != "" { 39 src += "/" + alert.Source.AsNumber 40 } 41 src += ")" 42 } 43 } else if *alert.Source.Scope == types.Range { 44 src = fmt.Sprintf("range %s", *alert.Source.Value) 45 if alert.Source.Cn != "" { 46 src += " (" + alert.Source.Cn 47 if alert.Source.AsNumber != "" { 48 src += "/" + alert.Source.AsNumber 49 } 50 src += ")" 51 } 52 } else { 53 src = fmt.Sprintf("%s %s", *alert.Source.Scope, *alert.Source.Value) 54 } 55 } else { 56 src = "empty source" 57 } 58 59 /**/ 60 reason := "" 61 if *alert.Scenario != "" { 62 reason = fmt.Sprintf("%s by %s", *alert.Scenario, src) 63 } else if *alert.Message != "" { 64 reason = fmt.Sprintf("%s by %s", *alert.Scenario, src) 65 } else { 66 reason = fmt.Sprintf("empty scenario by %s", src) 67 } 68 69 if len(alert.Decisions) > 0 { 70 for _, decisionItem := range alert.Decisions { 71 decision := "" 72 if alert.Simulated != nil && *alert.Simulated { 73 decision = "(simulated alert)" 74 } else if decisionItem.Simulated != nil && *decisionItem.Simulated { 75 decision = "(simulated decision)" 76 } 77 if log.GetLevel() >= log.DebugLevel { 78 /*spew is expensive*/ 79 log.Debugf("%s", spew.Sdump(decisionItem)) 80 } 81 decision += fmt.Sprintf("%s %s on %s %s", *decisionItem.Duration, 82 *decisionItem.Type, *decisionItem.Scope, *decisionItem.Value) 83 retStr = append(retStr, 84 fmt.Sprintf("(%s/%s) %s : %s", machineId, 85 *decisionItem.Origin, reason, decision)) 86 } 87 } else { 88 retStr = append(retStr, fmt.Sprintf("(%s) alert : %s", machineId, reason)) 89 } 90 return retStr 91 } 92 93 func (c *Client) CreateAlert(machineID string, alertList []*models.Alert) ([]string, error) { 94 pageStart := 0 95 pageEnd := bulkSize 96 ret := []string{} 97 for { 98 if pageEnd >= len(alertList) { 99 results, err := c.CreateAlertBulk(machineID, alertList[pageStart:]) 100 if err != nil { 101 return []string{}, fmt.Errorf("unable to create alerts: %s", err) 102 } 103 ret = append(ret, results...) 104 break 105 } 106 results, err := c.CreateAlertBulk(machineID, alertList[pageStart:pageEnd]) 107 if err != nil { 108 return []string{}, fmt.Errorf("unable to create alerts: %s", err) 109 } 110 ret = append(ret, results...) 111 pageStart += bulkSize 112 pageEnd += bulkSize 113 } 114 return ret, nil 115 } 116 117 func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([]string, error) { 118 119 ret := []string{} 120 bulkSize := 20 121 122 c.Log.Debugf("writting %d items", len(alertList)) 123 bulk := make([]*ent.AlertCreate, 0, bulkSize) 124 for i, alertItem := range alertList { 125 var decisions []*ent.Decision 126 var metas []*ent.Meta 127 var events []*ent.Event 128 129 owner, err := c.QueryMachineByID(machineId) 130 if err != nil { 131 if errors.Cause(err) != UserNotExists { 132 return []string{}, errors.Wrapf(QueryFail, "machine '%s': %s", alertItem.MachineID, err) 133 } 134 c.Log.Debugf("CreateAlertBulk: Machine Id %s doesn't exist", machineId) 135 owner = nil 136 } 137 startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt) 138 if err != nil { 139 return []string{}, errors.Wrapf(ParseTimeFail, "start_at field time '%s': %s", *alertItem.StartAt, err) 140 } 141 142 stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt) 143 if err != nil { 144 return []string{}, errors.Wrapf(ParseTimeFail, "stop_at field time '%s': %s", *alertItem.StopAt, err) 145 } 146 /*display proper alert in logs*/ 147 for _, disp := range formatAlertAsString(machineId, alertItem) { 148 c.Log.Info(disp) 149 } 150 151 if len(alertItem.Events) > 0 { 152 eventBulk := make([]*ent.EventCreate, len(alertItem.Events)) 153 for i, eventItem := range alertItem.Events { 154 ts, err := time.Parse(time.RFC3339, *eventItem.Timestamp) 155 if err != nil { 156 return []string{}, errors.Wrapf(ParseTimeFail, "event timestamp '%s' : %s", *eventItem.Timestamp, err) 157 } 158 marshallMetas, err := json.Marshal(eventItem.Meta) 159 if err != nil { 160 return []string{}, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err) 161 } 162 163 eventBulk[i] = c.Ent.Event.Create(). 164 SetTime(ts). 165 SetSerialized(string(marshallMetas)) 166 } 167 events, err = c.Ent.Event.CreateBulk(eventBulk...).Save(c.CTX) 168 if err != nil { 169 return []string{}, errors.Wrapf(BulkError, "creating alert events: %s", err) 170 } 171 } 172 173 if len(alertItem.Meta) > 0 { 174 metaBulk := make([]*ent.MetaCreate, len(alertItem.Meta)) 175 for i, metaItem := range alertItem.Meta { 176 metaBulk[i] = c.Ent.Meta.Create(). 177 SetKey(metaItem.Key). 178 SetValue(metaItem.Value) 179 } 180 metas, err = c.Ent.Meta.CreateBulk(metaBulk...).Save(c.CTX) 181 if err != nil { 182 return []string{}, errors.Wrapf(BulkError, "creating alert meta: %s", err) 183 } 184 } 185 186 ts, err := time.Parse(time.RFC3339, *alertItem.StopAt) 187 if err != nil { 188 c.Log.Errorf("While parsing StartAt of item %s : %s", *alertItem.StopAt, err) 189 ts = time.Now() 190 } 191 if len(alertItem.Decisions) > 0 { 192 decisionBulk := make([]*ent.DecisionCreate, len(alertItem.Decisions)) 193 for i, decisionItem := range alertItem.Decisions { 194 var start_ip, start_sfx, end_ip, end_sfx int64 195 var sz int 196 197 duration, err := time.ParseDuration(*decisionItem.Duration) 198 if err != nil { 199 return []string{}, errors.Wrapf(ParseDurationFail, "decision duration '%v' : %s", decisionItem.Duration, err) 200 } 201 202 /*if the scope is IP or Range, convert the value to integers */ 203 if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" { 204 sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value) 205 if err != nil { 206 return []string{}, errors.Wrapf(ParseDurationFail, "invalid addr/range %s : %s", *decisionItem.Value, err) 207 } 208 } 209 decisionBulk[i] = c.Ent.Decision.Create(). 210 SetUntil(ts.Add(duration)). 211 SetScenario(*decisionItem.Scenario). 212 SetType(*decisionItem.Type). 213 SetStartIP(start_ip). 214 SetStartSuffix(start_sfx). 215 SetEndIP(end_ip). 216 SetEndSuffix(end_sfx). 217 SetIPSize(int64(sz)). 218 SetValue(*decisionItem.Value). 219 SetScope(*decisionItem.Scope). 220 SetOrigin(*decisionItem.Origin). 221 SetSimulated(*alertItem.Simulated) 222 } 223 decisions, err = c.Ent.Decision.CreateBulk(decisionBulk...).Save(c.CTX) 224 if err != nil { 225 return []string{}, errors.Wrapf(BulkError, "creating alert decisions: %s", err) 226 227 } 228 } 229 230 alertB := c.Ent.Alert. 231 Create(). 232 SetScenario(*alertItem.Scenario). 233 SetMessage(*alertItem.Message). 234 SetEventsCount(*alertItem.EventsCount). 235 SetStartedAt(startAtTime). 236 SetStoppedAt(stopAtTime). 237 SetSourceScope(*alertItem.Source.Scope). 238 SetSourceValue(*alertItem.Source.Value). 239 SetSourceIp(alertItem.Source.IP). 240 SetSourceRange(alertItem.Source.Range). 241 SetSourceAsNumber(alertItem.Source.AsNumber). 242 SetSourceAsName(alertItem.Source.AsName). 243 SetSourceCountry(alertItem.Source.Cn). 244 SetSourceLatitude(alertItem.Source.Latitude). 245 SetSourceLongitude(alertItem.Source.Longitude). 246 SetCapacity(*alertItem.Capacity). 247 SetLeakSpeed(*alertItem.Leakspeed). 248 SetSimulated(*alertItem.Simulated). 249 SetScenarioVersion(*alertItem.ScenarioVersion). 250 SetScenarioHash(*alertItem.ScenarioHash). 251 AddDecisions(decisions...). 252 AddEvents(events...). 253 AddMetas(metas...) 254 255 if owner != nil { 256 alertB.SetOwner(owner) 257 } 258 bulk = append(bulk, alertB) 259 260 if len(bulk) == bulkSize { 261 alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX) 262 if err != nil { 263 return []string{}, errors.Wrapf(BulkError, "bulk creating alert : %s", err) 264 } 265 for _, alert := range alerts { 266 ret = append(ret, strconv.Itoa(alert.ID)) 267 } 268 269 if len(alertList)-i <= bulkSize { 270 bulk = make([]*ent.AlertCreate, 0, (len(alertList) - i)) 271 } else { 272 bulk = make([]*ent.AlertCreate, 0, bulkSize) 273 } 274 } 275 } 276 277 alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX) 278 if err != nil { 279 return []string{}, errors.Wrapf(BulkError, "leftovers creating alert : %s", err) 280 } 281 282 for _, alert := range alerts { 283 ret = append(ret, strconv.Itoa(alert.ID)) 284 } 285 286 return ret, nil 287 } 288 289 func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) { 290 var err error 291 var start_ip, start_sfx, end_ip, end_sfx int64 292 var hasActiveDecision bool 293 var ip_sz int 294 var contains bool = true 295 /*if contains is true, return bans that *contains* the given value (value is the inner) 296 else, return bans that are *contained* by the given value (value is the outer)*/ 297 298 /*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */ 299 if v, ok := filter["simulated"]; ok { 300 if v[0] == "false" { 301 alerts = alerts.Where(alert.SimulatedEQ(false)) 302 } 303 delete(filter, "simulated") 304 } 305 306 for param, value := range filter { 307 switch param { 308 case "contains": 309 contains, err = strconv.ParseBool(value[0]) 310 if err != nil { 311 return nil, errors.Wrapf(InvalidFilter, "invalid contains value : %s", err) 312 } 313 case "scope": 314 var scope string = value[0] 315 if strings.ToLower(scope) == "ip" { 316 scope = types.Ip 317 } else if strings.ToLower(scope) == "range" { 318 scope = types.Range 319 } 320 alerts = alerts.Where(alert.SourceScopeEQ(scope)) 321 case "value": 322 alerts = alerts.Where(alert.SourceValueEQ(value[0])) 323 case "scenario": 324 alerts = alerts.Where(alert.ScenarioEQ(value[0])) 325 case "ip", "range": 326 ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0]) 327 if err != nil { 328 return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err) 329 } 330 case "since": 331 duration, err := types.ParseDuration(value[0]) 332 if err != nil { 333 return nil, errors.Wrap(err, "while parsing duration") 334 } 335 since := time.Now().Add(-duration) 336 if since.IsZero() { 337 return nil, fmt.Errorf("Empty time now() - %s", since.String()) 338 } 339 alerts = alerts.Where(alert.StartedAtGTE(since)) 340 case "created_before": 341 duration, err := types.ParseDuration(value[0]) 342 if err != nil { 343 return nil, errors.Wrap(err, "while parsing duration") 344 } 345 since := time.Now().Add(-duration) 346 if since.IsZero() { 347 return nil, fmt.Errorf("Empty time now() - %s", since.String()) 348 } 349 alerts = alerts.Where(alert.CreatedAtLTE(since)) 350 case "until": 351 duration, err := types.ParseDuration(value[0]) 352 if err != nil { 353 return nil, errors.Wrap(err, "while parsing duration") 354 } 355 until := time.Now().Add(-duration) 356 if until.IsZero() { 357 return nil, fmt.Errorf("Empty time now() - %s", until.String()) 358 } 359 alerts = alerts.Where(alert.StartedAtLTE(until)) 360 case "decision_type": 361 alerts = alerts.Where(alert.HasDecisionsWith(decision.TypeEQ(value[0]))) 362 case "has_active_decision": 363 if hasActiveDecision, err = strconv.ParseBool(value[0]); err != nil { 364 return nil, errors.Wrapf(ParseType, "'%s' is not a boolean: %s", value[0], err) 365 } 366 if hasActiveDecision { 367 alerts = alerts.Where(alert.HasDecisionsWith(decision.UntilGTE(time.Now()))) 368 } else { 369 alerts = alerts.Where(alert.Not(alert.HasDecisions())) 370 } 371 case "limit": 372 continue 373 case "sort": 374 continue 375 default: 376 return nil, errors.Wrapf(InvalidFilter, "Filter parameter '%s' is unknown (=%s)", param, value[0]) 377 } 378 } 379 380 if ip_sz == 4 { 381 if contains { /*decision contains {start_ip,end_ip}*/ 382 alerts = alerts.Where(alert.And( 383 alert.HasDecisionsWith(decision.StartIPLTE(start_ip)), 384 alert.HasDecisionsWith(decision.EndIPGTE(end_ip)), 385 alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))), 386 )) 387 } else { /*decision is contained within {start_ip,end_ip}*/ 388 alerts = alerts.Where(alert.And( 389 alert.HasDecisionsWith(decision.StartIPGTE(start_ip)), 390 alert.HasDecisionsWith(decision.EndIPLTE(end_ip)), 391 alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))), 392 )) 393 } 394 } else if ip_sz == 16 { 395 396 if contains { /*decision contains {start_ip,end_ip}*/ 397 alerts = alerts.Where(alert.And( 398 //matching addr size 399 alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))), 400 alert.Or( 401 //decision.start_ip < query.start_ip 402 alert.HasDecisionsWith(decision.StartIPLT(start_ip)), 403 alert.And( 404 //decision.start_ip == query.start_ip 405 alert.HasDecisionsWith(decision.StartIPEQ(start_ip)), 406 //decision.start_suffix <= query.start_suffix 407 alert.HasDecisionsWith(decision.StartSuffixLTE(start_sfx)), 408 )), 409 alert.Or( 410 //decision.end_ip > query.end_ip 411 alert.HasDecisionsWith(decision.EndIPGT(end_ip)), 412 alert.And( 413 //decision.end_ip == query.end_ip 414 alert.HasDecisionsWith(decision.EndIPEQ(end_ip)), 415 //decision.end_suffix >= query.end_suffix 416 alert.HasDecisionsWith(decision.EndSuffixGTE(end_sfx)), 417 ), 418 ), 419 )) 420 } else { /*decision is contained within {start_ip,end_ip}*/ 421 alerts = alerts.Where(alert.And( 422 //matching addr size 423 alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))), 424 alert.Or( 425 //decision.start_ip > query.start_ip 426 alert.HasDecisionsWith(decision.StartIPGT(start_ip)), 427 alert.And( 428 //decision.start_ip == query.start_ip 429 alert.HasDecisionsWith(decision.StartIPEQ(start_ip)), 430 //decision.start_suffix >= query.start_suffix 431 alert.HasDecisionsWith(decision.StartSuffixGTE(start_sfx)), 432 )), 433 alert.Or( 434 //decision.end_ip < query.end_ip 435 alert.HasDecisionsWith(decision.EndIPLT(end_ip)), 436 alert.And( 437 //decision.end_ip == query.end_ip 438 alert.HasDecisionsWith(decision.EndIPEQ(end_ip)), 439 //decision.end_suffix <= query.end_suffix 440 alert.HasDecisionsWith(decision.EndSuffixLTE(end_sfx)), 441 ), 442 ), 443 )) 444 } 445 } else if ip_sz != 0 { 446 return nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz) 447 } 448 return alerts, nil 449 } 450 451 func (c *Client) TotalAlerts() (int, error) { 452 return c.Ent.Alert.Query().Count(c.CTX) 453 } 454 455 func (c *Client) QueryAlertWithFilter(filter map[string][]string) ([]*ent.Alert, error) { 456 sort := "DESC" // we sort by desc by default 457 if val, ok := filter["sort"]; ok { 458 if val[0] != "ASC" && val[0] != "DESC" { 459 c.Log.Errorf("invalid 'sort' parameter: %s", val) 460 } else { 461 sort = val[0] 462 } 463 } 464 limit := defaultLimit 465 if val, ok := filter["limit"]; ok { 466 limitConv, err := strconv.Atoi(val[0]) 467 if err != nil { 468 return []*ent.Alert{}, errors.Wrapf(QueryFail, "bad limit in parameters: %s", val) 469 } 470 limit = limitConv 471 472 } 473 offset := 0 474 ret := make([]*ent.Alert, 0) 475 for { 476 alerts := c.Ent.Alert.Query() 477 alerts, err := BuildAlertRequestFromFilter(alerts, filter) 478 if err != nil { 479 return []*ent.Alert{}, err 480 } 481 alerts = alerts. 482 WithDecisions(). 483 WithEvents(). 484 WithMetas(). 485 WithOwner() 486 if sort == "ASC" { 487 alerts = alerts.Order(ent.Asc(alert.FieldCreatedAt)) 488 } else { 489 alerts = alerts.Order(ent.Desc(alert.FieldCreatedAt)) 490 } 491 if limit == 0 { 492 limit, err = alerts.Count(c.CTX) 493 if err != nil { 494 return []*ent.Alert{}, fmt.Errorf("unable to count nb alerts: %s", err) 495 } 496 } 497 result, err := alerts.Limit(paginationSize).Offset(offset).All(c.CTX) 498 if err != nil { 499 return []*ent.Alert{}, errors.Wrapf(QueryFail, "pagination size: %d, offset: %d: %s", paginationSize, offset, err) 500 } 501 if diff := limit - len(ret); diff < paginationSize { 502 if len(result) < diff { 503 ret = append(ret, result...) 504 c.Log.Debugf("Pagination done, %d < %d", len(result), diff) 505 break 506 } 507 ret = append(ret, result[0:diff]...) 508 } else { 509 ret = append(ret, result...) 510 } 511 if len(ret) == limit || len(ret) == 0 { 512 c.Log.Debugf("Pagination done len(ret) = %d", len(ret)) 513 break 514 } 515 offset += paginationSize 516 } 517 518 return ret, nil 519 } 520 521 func (c *Client) DeleteAlertGraph(alertItem *ent.Alert) error { 522 // delete the associated events 523 _, err := c.Ent.Event.Delete(). 524 Where(event.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX) 525 if err != nil { 526 c.Log.Warningf("DeleteAlertGraph : %s", err) 527 return errors.Wrapf(DeleteFail, "event with alert ID '%d'", alertItem.ID) 528 } 529 530 // delete the associated meta 531 _, err = c.Ent.Meta.Delete(). 532 Where(meta.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX) 533 if err != nil { 534 c.Log.Warningf("DeleteAlertGraph : %s", err) 535 return errors.Wrapf(DeleteFail, "meta with alert ID '%d'", alertItem.ID) 536 } 537 538 // delete the associated decisions 539 _, err = c.Ent.Decision.Delete(). 540 Where(decision.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX) 541 if err != nil { 542 c.Log.Warningf("DeleteAlertGraph : %s", err) 543 return errors.Wrapf(DeleteFail, "decision with alert ID '%d'", alertItem.ID) 544 } 545 546 // delete the alert 547 err = c.Ent.Alert.DeleteOne(alertItem).Exec(c.CTX) 548 if err != nil { 549 c.Log.Warningf("DeleteAlertGraph : %s", err) 550 return errors.Wrapf(DeleteFail, "alert with ID '%d'", alertItem.ID) 551 } 552 553 return nil 554 } 555 556 func (c *Client) DeleteAlertWithFilter(filter map[string][]string) (int, error) { 557 var err error 558 559 // Get all the alerts that match the filter 560 alertsToDelete, err := c.QueryAlertWithFilter(filter) 561 562 for _, alertItem := range alertsToDelete { 563 err = c.DeleteAlertGraph(alertItem) 564 if err != nil { 565 c.Log.Warningf("DeleteAlertWithFilter : %s", err) 566 return 0, errors.Wrapf(DeleteFail, "event with alert ID '%d'", alertItem.ID) 567 } 568 } 569 return len(alertsToDelete), nil 570 } 571 572 func (c *Client) FlushAlerts(MaxAge string, MaxItems int) error { 573 var deletedByAge int 574 var deletedByNbItem int 575 var totalAlerts int 576 var err error 577 totalAlerts, err = c.TotalAlerts() 578 if err != nil { 579 c.Log.Warningf("FlushAlerts (max items count) : %s", err) 580 return errors.Wrap(err, "unable to get alerts count") 581 } 582 if MaxAge != "" { 583 filter := map[string][]string{ 584 "created_before": {MaxAge}, 585 } 586 nbDeleted, err := c.DeleteAlertWithFilter(filter) 587 if err != nil { 588 c.Log.Warningf("FlushAlerts (max age) : %s", err) 589 return errors.Wrapf(err, "unable to flush alerts with filter until: %s", MaxAge) 590 } 591 deletedByAge = nbDeleted 592 } 593 if MaxItems > 0 { 594 if totalAlerts > MaxItems { 595 nbToDelete := totalAlerts - MaxItems 596 alerts, err := c.QueryAlertWithFilter(map[string][]string{ 597 "sort": {"ASC"}, 598 "limit": {strconv.Itoa(nbToDelete)}, 599 }) // we want to delete older alerts if we reach the max number of items 600 if err != nil { 601 c.Log.Warningf("FlushAlerts (max items query) : %s", err) 602 return errors.Wrap(err, "unable to get all alerts") 603 } 604 for itemNb, alert := range alerts { 605 if itemNb < nbToDelete { 606 err := c.DeleteAlertGraph(alert) 607 if err != nil { 608 c.Log.Warningf("FlushAlerts : %s", err) 609 return errors.Wrap(err, "unable to flush alert") 610 } 611 deletedByNbItem++ 612 } 613 } 614 } 615 } 616 if deletedByNbItem > 0 { 617 c.Log.Infof("flushed %d/%d alerts because max number of alerts has been reached (%d max)", deletedByNbItem, totalAlerts, MaxItems) 618 } 619 if deletedByAge > 0 { 620 c.Log.Infof("flushed %d/%d alerts because they were created %s ago or more", deletedByAge, totalAlerts, MaxAge) 621 } 622 return nil 623 } 624 625 func (c *Client) GetAlertByID(alertID int) (*ent.Alert, error) { 626 alert, err := c.Ent.Alert.Query().Where(alert.IDEQ(alertID)).WithDecisions().WithEvents().WithMetas().WithOwner().First(c.CTX) 627 if err != nil { 628 /*record not found, 404*/ 629 if ent.IsNotFound(err) { 630 log.Warningf("GetAlertByID (not found): %s", err) 631 return &ent.Alert{}, ItemNotFound 632 } 633 c.Log.Warningf("GetAlertByID : %s", err) 634 return &ent.Alert{}, QueryFail 635 } 636 return alert, nil 637 }