github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/config/alerts.go (about) 1 package config 2 3 import ( 4 "strings" 5 "time" 6 7 "github.com/apex/up/internal/util" 8 "github.com/apex/up/internal/validate" 9 "github.com/pkg/errors" 10 ) 11 12 // types of action. 13 var types = []string{ 14 "slack", 15 "email", 16 "sms", 17 } 18 19 // namespace mappings. 20 var namespaceMap = map[string]string{ 21 "http": "AWS/ApiGateway", 22 } 23 24 // metrics mappings. 25 var metricsMap = map[string]string{ 26 "http.count": "Count", 27 "http.latency": "Latency", 28 "http.4xx": "4XXError", 29 "http.5xx": "5XXError", 30 } 31 32 // operator mappings. 33 var operatorMap = map[string]string{ 34 ">": "GreaterThanThreshold", 35 "<": "LessThanThreshold", 36 ">=": "GreaterThanOrEqualToThreshold", 37 "<=": "LessThanOrEqualToThreshold", 38 } 39 40 // statistic mappings. 41 var statisticMap = map[string]string{ 42 "count": "SampleCount", 43 "sum": "Sum", 44 "avg": "Average", 45 "average": "Average", 46 "min": "Minimum", 47 "minimum": "Minimum", 48 "max": "Maximum", 49 "maximum": "Maximum", 50 } 51 52 // missingData options. 53 var missingData = []string{ 54 "breaching", 55 "notBreaching", 56 "ignore", 57 "missing", 58 } 59 60 // AlertAction config. 61 type AlertAction struct { 62 // Name of the action. 63 Name string `json:"name"` 64 65 // Type of action. 66 Type string `json:"type"` 67 68 // Emails for email action. 69 Emails []string `json:"emails"` 70 71 // Numbers for sms action. 72 Numbers []string `json:"numbers"` 73 74 // URL for slack webhook action. 75 URL string `json:"url"` 76 77 // Channel for slack webhook action (optional). 78 Channel string `json:"channel"` 79 80 // Gifs enabled for alerts. 81 Gifs bool `json:"gifs"` 82 } 83 84 // Validate implementation. 85 func (a *AlertAction) Validate() error { 86 if err := validate.RequiredString(a.Name); err != nil { 87 return errors.Wrap(err, ".name") 88 } 89 90 if err := validate.List(a.Type, types); err != nil { 91 return errors.Wrap(err, ".type") 92 } 93 94 switch a.Type { 95 case "email": 96 if err := validate.MinStrings(a.Emails, 1); err != nil { 97 return errors.Wrap(err, ".emails") 98 } 99 case "sms": 100 if err := validate.MinStrings(a.Numbers, 1); err != nil { 101 return errors.Wrap(err, ".numbers") 102 } 103 case "slack": 104 if err := validate.RequiredString(a.URL); err != nil { 105 return errors.Wrap(err, ".url") 106 } 107 } 108 109 return nil 110 } 111 112 // Alert config. 113 type Alert struct { 114 Description string `json:"description"` 115 Disable bool `json:"disable"` 116 Metric string `json:"metric"` 117 Namespace string `json:"namespace"` 118 Statistic string `json:"statistic"` 119 Operator string `json:"operator"` 120 Threshold int `json:"threshold"` 121 Period Duration `json:"period"` // TODO: must be multiple of 60? 122 EvaluationPeriods int `json:"evaluation_periods"` 123 Stage string `json:"stage"` 124 Action string `json:"action"` 125 Missing string `json:"missing"` 126 } 127 128 // Default implementation. 129 func (a *Alert) Default() error { 130 if a.Operator == "" { 131 a.Operator = ">" 132 } 133 134 if a.Missing == "" { 135 a.Missing = "notBreaching" 136 } 137 138 if a.Period == 0 { 139 a.Period = Duration(time.Minute) 140 } 141 142 if a.EvaluationPeriods == 0 { 143 a.EvaluationPeriods = 1 144 } 145 146 if s := a.Metric; s != "" { 147 parts := strings.Split(a.Metric, ".") 148 149 if s, ok := namespaceMap[parts[0]]; ok { 150 a.Namespace = s 151 } 152 153 if s, ok := metricsMap[a.Metric]; ok { 154 a.Metric = s 155 } 156 } 157 158 return nil 159 } 160 161 // Validate implementation. 162 func (a *Alert) Validate() error { 163 if s, ok := operatorMap[a.Operator]; ok { 164 a.Operator = s 165 } else { 166 if err := validate.List(a.Operator, util.StringMapKeys(operatorMap)); err != nil { 167 return errors.Wrap(err, ".operator") 168 } 169 } 170 171 if s, ok := statisticMap[a.Statistic]; ok { 172 a.Statistic = s 173 } else { 174 if err := validate.List(a.Statistic, util.StringMapKeys(statisticMap)); err != nil { 175 return errors.Wrap(err, ".statistic") 176 } 177 } 178 179 if err := validate.List(a.Missing, missingData); err != nil { 180 return errors.Wrap(err, ".missing") 181 } 182 183 if err := validate.RequiredString(a.Metric); err != nil { 184 return errors.Wrap(err, ".metric") 185 } 186 187 if err := validate.RequiredString(a.Namespace); err != nil { 188 return errors.Wrap(err, ".namespace") 189 } 190 191 return nil 192 } 193 194 // Alerting config. 195 type Alerting struct { 196 Actions []*AlertAction `json:"actions"` 197 Alerts []*Alert `json:"alerts"` 198 } 199 200 // Default implementation. 201 func (a *Alerting) Default() error { 202 for i, a := range a.Alerts { 203 if err := a.Default(); err != nil { 204 return errors.Wrapf(err, ".actions %d", i) 205 } 206 } 207 208 return nil 209 } 210 211 // Validate implementation. 212 func (a *Alerting) Validate() error { 213 for i, a := range a.Actions { 214 if err := a.Validate(); err != nil { 215 return errors.Wrapf(err, ".actions %d", i) 216 } 217 } 218 219 for i, alert := range a.Alerts { 220 if err := alert.Validate(); err != nil { 221 return errors.Wrapf(err, ".actions %d", i) 222 } 223 224 if a.GetActionByName(alert.Action) == nil { 225 return errors.Errorf(".action %q is not defined", alert.Action) 226 } 227 } 228 229 return nil 230 } 231 232 // GetActionByName returns the action by name or nil. 233 func (a *Alerting) GetActionByName(name string) *AlertAction { 234 for _, action := range a.Actions { 235 if action.Name == name { 236 return action 237 } 238 } 239 240 return nil 241 }