mattermost-notifier.go
1 package notifier 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 log "github.com/AcalephStorage/consul-alerts/Godeps/_workspace/src/github.com/Sirupsen/logrus" 8 "net/http" 9 "strconv" 10 "strings" 11 ) 12 13 type StringMap map[string]string 14 15 type MattermostLoginInfo struct { 16 LoginID string `json:"login_id"` 17 Password string `json:"password"` 18 } 19 20 type MattermostAuthInfo struct { 21 UserID string `json:"id"` 22 CreateAt int64 `json:"create_at"` 23 UpdateAt int64 `json:"update_at"` 24 DeleteAt int64 `json:"delete_at"` 25 UserName string `json:"username"` 26 AuthData string `json:"auth_data"` 27 AuthService string `json:"auth_service"` 28 Email string `json:"email"` 29 EmailVerified bool `json:"email_verified"` 30 NickName string `json:"nickname"` 31 FirstName string `json:"first_name"` 32 LastName string `json:"last_name"` 33 Roles string `json:"roles"` 34 AllowMarketing bool `json:"allow_marketing"` 35 NotifyProps StringMap `json:"notify_props"` 36 Props StringMap `json:"props"` 37 LastPasswordUpdate int64 `json:"last_password_update"` 38 LastPictureUpdate int64 `json:"last_picture_update"` 39 } 40 41 type MattermostUserInfo struct { 42 UserID string `json:"id"` 43 CreateAt int64 `json:"create_at"` 44 UpdateAt int64 `json:"update_at"` 45 DeleteAt int64 `json:"delete_at"` 46 Username string `json:"username"` 47 FirstName string `json:"first_name"` 48 LastName string `json:"last_name"` 49 Nickname string `json:"nickname"` 50 Email string `json:"email"` 51 EmailVerified bool `json:"email_verified"` 52 Password string `json:"password"` 53 AuthData *string `json:"auth_data"` 54 AuthService string `json:"auth_service"` 55 Roles string `json:"roles"` 56 NotifyProps StringMap `json:"notify_props"` 57 Props StringMap `json:"props,omitempty"` 58 LastPasswordUpdate int64 `json:"last_password_update"` 59 LastPictureUpdate int64 `json:"last_picture_update"` 60 FailedAttempts int `json:"failed_attempts"` 61 MfaActive bool `json:"mfa_active"` 62 MfaSecret string `json:"mfa_secret"` 63 } 64 65 type MattermostTeamInfo struct { 66 TeamID string `json:"id"` 67 CreateAt int64 `json:"create_at"` 68 UpdateAt int64 `json:"update_at"` 69 DeleteAt int64 `json:"delete_at"` 70 DisplayName string `json:"display_name"` 71 Name string `json:"name"` 72 Email string `json:"email"` 73 Type string `json:"type"` 74 AllowedDomains string `json:"allowed_domains"` 75 InviteID string `json:"invite_id"` 76 AllowOpenInvite bool `json:"allow_open_invite"` 77 } 78 79 type MattermostChannelInfo struct { 80 ChannelID string `json:"id"` 81 CreateAt int64 `json:"create_at"` 82 UpdateAt int64 `json:"update_at"` 83 DeleteAt int64 `json:"delete_at"` 84 TeamID string `json:"team_id"` 85 Type string `json:"type"` 86 DisplayName string `json:"display_name"` 87 Name string `json:"name"` 88 Header string `json:"header"` 89 Purpose string `json:"purpose"` 90 LastPostAt int64 `json:"last_post_at"` 91 TotalMsgCount int64 `json:"total_msg_count"` 92 ExtraUpdateAt int64 `json:"extra_update_at"` 93 CreatorID string `json:"creator_id"` 94 } 95 96 type MattermostChannelList struct { 97 Channels []MattermostChannelInfo 98 } 99 100 type MattermostPostInfo struct { 101 PostID string `json:"id"` 102 CreateAt int64 `json:"create_at"` 103 UpdateAt int64 `json:"update_at"` 104 DeleteAt int64 `json:"delete_at"` 105 UserID string `json:"user_id"` 106 ChannelID string `json:"channel_id"` 107 RootID string `json:"root_id"` 108 ParentID string `json:"parent_id"` 109 OriginalID string `json:"original_id"` 110 Message string `json:"message"` 111 Type string `json:"type"` 112 Props StringMap `json:"props"` 113 Hashtags string `json:"hashtags"` 114 Filenames StringMap `json:"filenames"` 115 PendingPostID string `json:"pending_post_id"` 116 } 117 118 type MattermostNotifier struct { 119 ClusterName string 120 Url string 121 UserName string 122 Password string 123 Team string 124 Channel string 125 Detailed bool 126 NotifName string 127 Enabled bool 128 129 /* Filled in after authentication */ 130 Initialized bool 131 Token string 132 TeamID string 133 UserID string 134 ChannelID string 135 Text string 136 } 137 138 func (mattermost *MattermostNotifier) GetURL() string { 139 140 proto := "http" 141 u := strings.TrimSpace(strings.ToLower(mattermost.Url)) 142 if u[:5] == "https" && u[5] == ':' { 143 proto = "https" 144 } 145 146 host := "" 147 port := 0 148 buf := strings.Split(u, ":") 149 if (u[:4] == "http" && u[4] == ':') || 150 (u[:5] == "https" && u[5] == ':') { 151 152 host = strings.Trim(buf[1], "/") 153 if len(buf) == 3 { 154 port, _ = strconv.Atoi(strings.TrimSpace(buf[2])) 155 } 156 157 } else if len(buf) == 2 { 158 host = strings.Trim(buf[0], "/") 159 port, _ = strconv.Atoi(strings.TrimSpace(buf[1])) 160 161 } else { 162 host = strings.TrimSpace(buf[0]) 163 } 164 165 portstr := "" 166 if port > 0 { 167 portstr = fmt.Sprintf(":%d", port) 168 } 169 170 return fmt.Sprintf("%s://%s%s/api/v3", proto, host, portstr) 171 } 172 173 func (mattermost *MattermostNotifier) Authenticate() bool { 174 175 loginURL := fmt.Sprintf("%s/users/login", mattermost.GetURL()) 176 loginInfo := MattermostLoginInfo{LoginID: mattermost.UserName, 177 Password: mattermost.Password} 178 179 buf := new(bytes.Buffer) 180 json.NewEncoder(buf).Encode(loginInfo) 181 182 req, err := http.NewRequest("POST", loginURL, buf) 183 if err != nil { 184 log.Error("NewRequest: ", err) 185 return false 186 } 187 188 req.Header.Set("Content-Type", "application/json; charset=utf-8") 189 190 client := &http.Client{} 191 resp, err := client.Do(req) 192 if err != nil { 193 log.Error("Do: ", err) 194 return false 195 } 196 197 defer resp.Body.Close() 198 199 decoder := json.NewDecoder(resp.Body) 200 var a MattermostAuthInfo 201 err = decoder.Decode(&a) 202 if err != nil { 203 log.Error("Decode: ", err) 204 return false 205 } 206 207 if buf, ok := resp.Header["Token"]; ok { 208 if len(buf) > 0 { 209 mattermost.Token = buf[0] 210 return true 211 } 212 } 213 214 return false 215 } 216 217 func (mattermost *MattermostNotifier) GetAllTeams(teams *[]MattermostTeamInfo) bool { 218 219 teamURL := fmt.Sprintf("%s/teams/all", mattermost.GetURL()) 220 req, err := http.NewRequest("GET", teamURL, nil) 221 if err != nil { 222 log.Error("NewRequest: ", err) 223 return false 224 } 225 226 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 227 req.Header.Set("Authorization", authorization) 228 req.Header.Set("Content-Type", "application/json; charset=utf-8") 229 230 client := &http.Client{} 231 resp, err := client.Do(req) 232 if err != nil { 233 log.Error("Do: ", err) 234 return false 235 } 236 237 defer resp.Body.Close() 238 239 decoder := json.NewDecoder(resp.Body) 240 var buf map[string]*MattermostTeamInfo 241 err = decoder.Decode(&buf) 242 if err != nil { 243 log.Error("Decode: ", err) 244 return false 245 } 246 247 if len(buf) > 0 { 248 for _, value := range buf { 249 *teams = append(*teams, *value) 250 } 251 return true 252 } 253 254 return false 255 } 256 257 func (mattermost *MattermostNotifier) GetUser(userID string, userInfo *MattermostUserInfo) bool { 258 259 if userID == "" || userInfo == nil { 260 return false 261 } 262 263 userURL := fmt.Sprintf("%s/users/%s/get", mattermost.GetURL(), userID) 264 265 req, err := http.NewRequest("GET", userURL, nil) 266 if err != nil { 267 log.Error("NewRequest: ", err) 268 return false 269 } 270 271 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 272 req.Header.Set("Authorization", authorization) 273 req.Header.Set("Content-Type", "application/json; charset=utf-8") 274 275 client := &http.Client{} 276 resp, err := client.Do(req) 277 if err != nil { 278 log.Error("Do: ", err) 279 return false 280 } 281 282 defer resp.Body.Close() 283 284 decoder := json.NewDecoder(resp.Body) 285 err = decoder.Decode(userInfo) 286 if err != nil { 287 log.Error("Decode: ", err) 288 return false 289 } 290 291 return true 292 } 293 294 func (mattermost *MattermostNotifier) GetMe(me *MattermostUserInfo) bool { 295 296 if me == nil { 297 return false 298 } 299 300 userURL := fmt.Sprintf("%s/users/me", mattermost.GetURL()) 301 302 req, err := http.NewRequest("GET", userURL, nil) 303 if err != nil { 304 log.Error("NewRequest: ", err) 305 return false 306 } 307 308 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 309 req.Header.Set("Authorization", authorization) 310 req.Header.Set("Content-Type", "application/json; charset=utf-8") 311 312 client := &http.Client{} 313 resp, err := client.Do(req) 314 if err != nil { 315 log.Error("Do: ", err) 316 return false 317 } 318 319 defer resp.Body.Close() 320 321 decoder := json.NewDecoder(resp.Body) 322 err = decoder.Decode(me) 323 if err != nil { 324 log.Error("Decode: ", err) 325 return false 326 } 327 328 return true 329 } 330 331 func (mattermost *MattermostNotifier) GetTeam(teamID string, teamInfo *MattermostTeamInfo) bool { 332 333 if teamID == "" || teamInfo == nil { 334 return false 335 } 336 337 teamURL := fmt.Sprintf("%s/teams/%s/me", mattermost.GetURL(), teamID) 338 339 req, err := http.NewRequest("GET", teamURL, nil) 340 if err != nil { 341 log.Error("NewRequest: ", err) 342 return false 343 } 344 345 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 346 req.Header.Set("Authorization", authorization) 347 req.Header.Set("Content-Type", "application/json; charset=utf-8") 348 349 client := &http.Client{} 350 resp, err := client.Do(req) 351 if err != nil { 352 log.Error("Do: ", err) 353 return false 354 } 355 356 defer resp.Body.Close() 357 358 decoder := json.NewDecoder(resp.Body) 359 err = decoder.Decode(teamInfo) 360 if err != nil { 361 log.Error("Decode: ", err) 362 return false 363 } 364 365 return true 366 } 367 368 func (mattermost *MattermostNotifier) GetChannels(teamID string, channels *[]MattermostChannelInfo) bool { 369 370 if teamID == "" || channels == nil { 371 return false 372 } 373 374 channelURL := fmt.Sprintf("%s/teams/%s/channels/", mattermost.GetURL(), teamID) 375 req, err := http.NewRequest("GET", channelURL, nil) 376 if err != nil { 377 log.Error("NewRequest: ", err) 378 return false 379 } 380 381 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 382 req.Header.Set("Authorization", authorization) 383 req.Header.Set("Content-Type", "application/json; charset=utf-8") 384 385 client := &http.Client{} 386 resp, err := client.Do(req) 387 if err != nil { 388 log.Error("Do: ", err) 389 return false 390 } 391 392 defer resp.Body.Close() 393 394 decoder := json.NewDecoder(resp.Body) 395 fc := &MattermostChannelList{} 396 err = decoder.Decode(&fc) 397 if err != nil { 398 log.Error("Decode: ", err) 399 return false 400 } 401 *channels = fc.Channels 402 403 return true 404 } 405 406 func (mattermost *MattermostNotifier) PostMessage(teamID string, channelID string, postInfo *MattermostPostInfo) bool { 407 408 if teamID == "" || channelID == "" || postInfo == nil { 409 return false 410 } 411 412 postURL := fmt.Sprintf("%s/teams/%s/channels/%s/posts/create", 413 mattermost.GetURL(), teamID, channelID) 414 415 buf := new(bytes.Buffer) 416 encoder := json.NewEncoder(buf) 417 err := encoder.Encode(*postInfo) 418 419 req, err := http.NewRequest("POST", postURL, buf) 420 if err != nil { 421 log.Error("NewRequest: ", err) 422 return false 423 } 424 425 authorization := fmt.Sprintf("Bearer %s", mattermost.Token) 426 req.Header.Set("Authorization", authorization) 427 req.Header.Set("Content-Type", "application/json; charset=utf-8") 428 429 client := &http.Client{} 430 resp, err := client.Do(req) 431 if err != nil { 432 log.Error("Do: ", err) 433 return false 434 } 435 436 defer resp.Body.Close() 437 438 decoder := json.NewDecoder(resp.Body) 439 var p MattermostPostInfo 440 err = decoder.Decode(&p) 441 if err != nil { 442 log.Error("Decode: ", err) 443 return false 444 } 445 *postInfo = p 446 447 return false 448 } 449 450 func (mattermost *MattermostNotifier) Init() bool { 451 if mattermost.Initialized == true { 452 return true 453 } 454 455 if mattermost.Token == "" && !mattermost.Authenticate() { 456 log.Println("Mattermost: Unable to authenticate!") 457 return false 458 } 459 460 if mattermost.TeamID == "" { 461 var teams []MattermostTeamInfo 462 463 if !mattermost.GetAllTeams(&teams) { 464 log.Println("Mattermost: Unable to get teams!") 465 return false 466 } 467 468 for i := 0; i < len(teams); i++ { 469 if teams[i].Name == mattermost.Team { 470 mattermost.TeamID = teams[i].TeamID 471 break 472 } 473 } 474 475 if mattermost.TeamID == "" { 476 log.Println("Mattermost: Unable to find team!") 477 return false 478 } 479 } 480 481 if mattermost.UserID == "" { 482 var me MattermostUserInfo 483 484 if !mattermost.GetMe(&me) { 485 log.Println("Mattermost: Unable to get user!") 486 return false 487 } 488 489 if me.UserID == "" { 490 log.Println("Mattermost: Unable to get user ID!") 491 return false 492 } 493 494 mattermost.UserID = me.UserID 495 } 496 497 if mattermost.ChannelID == "" { 498 var channels []MattermostChannelInfo 499 500 if !mattermost.GetChannels(mattermost.TeamID, &channels) { 501 log.Println("Mattermost: Unable to get channels!") 502 return false 503 } 504 505 for i := 0; i < len(channels); i++ { 506 if channels[i].Name == mattermost.Channel { 507 mattermost.ChannelID = channels[i].ChannelID 508 break 509 } 510 } 511 512 if mattermost.ChannelID == "" { 513 log.Println("Mattermost: Unable to find channel!") 514 return false 515 } 516 } 517 518 mattermost.Initialized = true 519 return true 520 } 521 522 // NotifierName provides name for notifier selection 523 func (mattermost *MattermostNotifier) NotifierName() string { 524 return "mattermost" 525 } 526 527 func (mattermost *MattermostNotifier) Copy() Notifier { 528 notifier := *mattermost 529 return ¬ifier 530 } 531 532 //Notify sends messages to the endpoint notifier 533 func (mattermost *MattermostNotifier) Notify(messages Messages) bool { 534 if !mattermost.Init() { 535 return false 536 } 537 538 if mattermost.Detailed { 539 return mattermost.notifyDetailed(messages) 540 } 541 542 return mattermost.notifySimple(messages) 543 } 544 545 func (mattermost *MattermostNotifier) notifySimple(messages Messages) bool { 546 overallStatus, pass, warn, fail := messages.Summary() 547 548 text := fmt.Sprintf(header, mattermost.ClusterName, overallStatus, fail, warn, pass) 549 550 for _, message := range messages { 551 text += fmt.Sprintf("\n%s:%s:%s is %s.", 552 message.Node, message.Service, message.Check, message.Status) 553 text += fmt.Sprintf("\n%s\n\n", message.Output) 554 } 555 556 mattermost.Text = text 557 558 return mattermost.postToMattermost() 559 } 560 561 func (mattermost *MattermostNotifier) notifyDetailed(messages Messages) bool { 562 563 overallStatus, pass, warn, fail := messages.Summary() 564 565 var emoji string 566 switch overallStatus { 567 case SYSTEM_HEALTHY: 568 emoji = ":white_check_mark:" 569 case SYSTEM_UNSTABLE: 570 emoji = ":question:" 571 case SYSTEM_CRITICAL: 572 emoji = ":x:" 573 default: 574 emoji = ":question:" 575 } 576 title := "Consul monitoring report" 577 pretext := fmt.Sprintf("%s %s is *%s*", emoji, mattermost.ClusterName, overallStatus) 578 579 detailedBody := "" 580 detailedBody += fmt.Sprintf("*Changes:* Fail = %d, Warn = %d, Pass = %d", 581 fail, warn, pass) 582 detailedBody += fmt.Sprintf("\n") 583 584 for _, message := range messages { 585 detailedBody += fmt.Sprintf("\n*[%s:%s]* %s is *%s.*", 586 message.Node, message.Service, message.Check, message.Status) 587 detailedBody += fmt.Sprintf("\n`%s`", strings.TrimSpace(message.Output)) 588 } 589 590 mattermost.Text = fmt.Sprintf("%s\n%s\n%s\n\n", title, pretext, detailedBody) 591 592 return mattermost.postToMattermost() 593 594 } 595 596 func (mattermost *MattermostNotifier) postToMattermost() bool { 597 var postInfo = MattermostPostInfo{ 598 ChannelID: mattermost.ChannelID, 599 Message: mattermost.Text} 600 601 return mattermost.PostMessage(mattermost.TeamID, mattermost.ChannelID, &postInfo) 602 }