Skip to content

Commit

Permalink
Copy over required digest functions from notify
Browse files Browse the repository at this point in the history
  • Loading branch information
rchapin committed Mar 18, 2024
1 parent 2b1509d commit 528de31
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
vendor/

# Output directory files
output/*.json
output/
196 changes: 196 additions & 0 deletions pkg/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package main
import (
"encoding/json"
"fmt"
"math"
"reflect"
"sort"
"strings"
"text/template"
"time"

"github.com/kentik/insights-api/pkg/greek"

Check failure on line 13 in pkg/functions.go

View workflow job for this annotation

GitHub Actions / Tests

no required module provides package github.com/kentik/insights-api/pkg/greek; to add it:
)

var TextTemplateFuncMap = template.FuncMap{
Expand All @@ -23,6 +27,11 @@ var TextTemplateFuncMap = template.FuncMap{
"timeRfc3339": timeRfc3339,
"join": join,
"joinWith": joinWith,

"min": min,
"importanceToColor": importanceToColor,
"importanceToEmoji": importanceToEmoji,
"importanceLabel": importanceLabel,
}

func tryParseTime(input string) (time.Time, error) {
Expand Down Expand Up @@ -112,3 +121,190 @@ func explodeJSONKeys(s string) string {
}
return "explodeJSONKeysErr"
}

func min(value, otherValue int) int {
if value < otherValue {
return value
}
return otherValue
}

func importanceToColor(severity ViewModelImportance) string {
if color, ok := ImportanceToColors[severity]; ok {
return color
}
return ""
}

func importanceToEmoji(severity ViewModelImportance) string {
if emoji, ok := ImportanceToEmojis[severity]; ok {
return emoji
}
return ""
}

func importanceName(severity ViewModelImportance) string {
if label, ok := ImportanceNames[severity]; ok {
return label
}
return ""
}

func importanceLabel(severity ViewModelImportance) string {
return strings.Title(importanceName(severity))
}

const (
SYNTHETICS_BGP_ROUTE_PREFIX = "ktappprotocol__ktrac_bgp_updates__INET_01"
SYNTHETICS_BGP_PREFIX_LENGTH = "ktappprotocol__ktrac_bgp_updates__INT05"
)

func (details EventViewModelDetails) FormatDimensions() EventViewModelDetails {
result := make(EventViewModelDetails, 0)

// setup names we want to add to result
names := make(map[string]struct{}, len(details))
for _, name := range details.Names() {
names[name] = struct{}{}
}

// combine BGP route prefix & length
_, hasRoutePrefix := names[SYNTHETICS_BGP_ROUTE_PREFIX]
_, hasPrefixLength := names[SYNTHETICS_BGP_PREFIX_LENGTH]
if hasRoutePrefix && hasPrefixLength {
routePrefix := details.GetValue(SYNTHETICS_BGP_ROUTE_PREFIX)
prefixLength := details.GetValue(SYNTHETICS_BGP_PREFIX_LENGTH)

combined := &EventViewModelDetail{
Name: "combined_route_prefix_length",
Label: "Route Prefix/Length",
Tag: "dimension",
Value: fmt.Sprintf("%s/%s", routePrefix, prefixLength),
}
result = append(result, combined)
delete(names, SYNTHETICS_BGP_ROUTE_PREFIX)
delete(names, SYNTHETICS_BGP_PREFIX_LENGTH)
}

// add the rest of details
for _, detail := range details {
if _, ok := names[detail.Name]; ok {
result = append(result, detail)
}
}
return result
}

func (details EventViewModelDetails) FormatMetrics() EventViewModelDetails {
result := make(EventViewModelDetails, 0)
for _, detail := range details {
if detail.Tag != "metric" {
result = append(result, detail)
continue
}

floatValue, err := toFloat(detail.Value)
if err != nil {
result = append(result, detail)
continue
}

// prevent showing fractions when unnecessary
stringValue := fmt.Sprintf("%.2f", floatValue)
if _, fraction := math.Modf(floatValue); fraction < 0.05 {
stringValue = fmt.Sprintf("%.0f", floatValue)
}

formatted := &EventViewModelDetail{
Name: detail.Name,
Label: detail.Label,
Tag: detail.Tag,
Value: stringValue,
}
// format bits with "greek" bytes
if detail.Name == "bits" {
converted := strings.Split(greek.FormatBytesGreek(floatValue, "bits/s"), " ")
if len(converted) == 2 {
formatted.Label = converted[1]
formatted.Value = converted[0]
}
}
result = append(result, formatted)
}
return result
}

func toFloat(value interface{}) (float64, error) {
switch v := value.(type) {
case float64:
return v, nil
case int:
return float64(v), nil
default:
return 0, fmt.Errorf("don't know how to convert %T to float64", v)
}
}

func (vm *NotificationViewModel) EventsGroupedByImportance() []*EventGroupsByImportance {
importanceToEvent := make(map[ViewModelImportance][]*EventViewModel)
for _, event := range vm.RawEvents {
importanceToEvent[event.Importance] = append(importanceToEvent[event.Importance], event)
}

byImportance := make([]*EventGroupsByImportance, 0, len(VieModelImportanceOrdered))
for _, importance := range VieModelImportanceOrdered {
if len(importanceToEvent[importance]) <= 0 {
continue
}
byImportance = append(byImportance, vm.GetGroupsByImportance(importance, importanceToEvent[importance]))
}

return byImportance
}

func (vm *NotificationViewModel) GetGroupsByImportance(importance ViewModelImportance, events []*EventViewModel) *EventGroupsByImportance {
groupNameToEvents := make(map[string][]*EventViewModel)

for _, event := range events {
groupNameToEvents[event.GroupName] = append(groupNameToEvents[event.GroupName], event)
}

groups := make([]*EventGroup, 0, len(groupNameToEvents))
for groupName, groupEvents := range groupNameToEvents {
// sort each group by abstract normalized date
sort.Slice(groupEvents, func(i, j int) bool {
return groupEvents[i].StartTime < groupEvents[j].StartTime // RFC3339 to the rescue
})
groups = append(groups, &EventGroup{
Name: groupName,
Url: vm.getGroupUrl(groupEvents),
Events: groupEvents,
})
}

return &EventGroupsByImportance{
Importance: importance,
Groups: groups,
Count: len(events),
}
}

func (vm *NotificationViewModel) getGroupUrl(events []*EventViewModel) string {
event := events[0]
if event.IsAlarm() {
if event.Details.Has("DashboardAlarmURL") {
return fmt.Sprintf("%v", event.Details.GetValue("DashboardAlarmURL"))
}
return fmt.Sprintf("%v", event.Details.GetValue("AttackLogURL"))
}
if event.IsSynthetic() {
return fmt.Sprintf("%v", event.Details.GetValue("SyntheticsTestURL"))
}
if event.IsMitigation() {
return fmt.Sprintf("%v", event.Details.GetValue("MitigationURL"))
}
if event.IsInsight() {
return fmt.Sprintf("%v", event.Details.GetValue("InsightsSeverityURL"))
}
return ""
}
2 changes: 1 addition & 1 deletion pkg/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func Test_AllExamples_Render(t *testing.T) {

result := buf.Bytes()
outputPath := fmt.Sprintf("../output/%s-%s", modelName, strings.TrimSuffix(entry.Name, ".tmpl"))
ioutil.WriteFile(outputPath, result, 0644)
ioutil.WriteFile(outputPath, result, 0o644)

if entry.IsJson {
var jsonValue interface{}
Expand Down
50 changes: 48 additions & 2 deletions pkg/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,51 @@ var VieModelImportanceOrdered = [...]ViewModelImportance{
ViewModelImportance_None,
}

var ImportanceNames = map[ViewModelImportance]string{
ViewModelImportance_None: "n/a",
ViewModelImportance_Healthy: "healthy",
ViewModelImportance_Notice: "notice",
ViewModelImportance_Minor: "minor",
ViewModelImportance_Warning: "warning",
ViewModelImportance_Major: "major",
ViewModelImportance_Severe: "severe",
ViewModelImportance_Critical: "critical",
}

var ImportanceToColors = map[ViewModelImportance]string{
ViewModelImportance_None: "#999999",
ViewModelImportance_Healthy: "#1E9E1E",
ViewModelImportance_Notice: "#157FF3",
ViewModelImportance_Minor: "#F29D49",
ViewModelImportance_Warning: "#EE7E0F",
ViewModelImportance_Major: "#DB3737",
ViewModelImportance_Severe: "#C23030",
ViewModelImportance_Critical: "#A82A2A",
}

var ImportanceToEmojis = map[ViewModelImportance]string{
ViewModelImportance_None: "",
ViewModelImportance_Healthy: ":warning:",
ViewModelImportance_Notice: ":warning:",
ViewModelImportance_Minor: ":warning:",
ViewModelImportance_Warning: ":warning:",
ViewModelImportance_Major: ":warning:",
ViewModelImportance_Severe: ":warning:",
ViewModelImportance_Critical: ":warning:",
}

type EventGroupsByImportance struct {
Importance ViewModelImportance
Groups []*EventGroup
Count int
}

type EventGroup struct {
Name string
Url string
Events []*EventViewModel
}

// use "export" key instead of standard json key when marshalling/unmarshalling using jsoniter (https://github.com/json-iterator/go),
// so fields are not removed per standard json tag
type EventViewModel struct {
Expand Down Expand Up @@ -202,8 +247,9 @@ type NotificationViewModel struct {
Config *NotificationViewConfig `json:"-" export:"Config"`
}
type NotificationViewConfig struct {
BaseDomain string
EmailTo []string
BaseDomain string
EmailTo []string
IsChannelDigest bool
}

func (vm *NotificationViewModel) BasePortalURL() string {
Expand Down

0 comments on commit 528de31

Please sign in to comment.